├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── assets └── installsnip.png ├── build ├── pwa-install.d.ts ├── pwa-install.js └── pwa-install.js.map ├── coverage ├── base.css ├── block-navigation.js ├── index.html ├── lcov.info ├── prettify.css ├── prettify.js ├── sort-arrow-sprite.png └── sorter.js ├── index.d.ts ├── karma.conf.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src └── pwa-install.ts ├── test └── basic │ └── basic.test.js ├── tsconfig.json └── www ├── index.html ├── manifest-invalid.json ├── manifest-lesser.json ├── manifest.json ├── service-worker.js └── style.css /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug :bug:' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: I have a question about something in PWA-Install 4 | title: "[Question]" 5 | labels: 'question :grey_question:' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Question in detail** 11 | A clear and concise description of what you want to know or confused about. 12 | 13 | **Is your question related to a problem? Please describe.** 14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions you've considered. 18 | 19 | **Additional context** 20 | Add any other details that will help understand the question. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | 4 | ## PR Type 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ## Describe the current behavior? 18 | 19 | 20 | 21 | ## Describe the new behavior? 22 | 23 | 24 | ## PR Checklist 25 | 26 | - [ ] Test: run `npm run test` and ensure that all tests pass 27 | - [ ] Target master branch (or an appropriate release branch if appropriate for a bug fix) 28 | - [ ] Ensure that your contribution follows [standard accessibility guidelines](https://docs.microsoft.com/en-us/microsoft-edge/accessibility/design). Use tools like https://webhint.io/ to validate your changes. 29 | 30 | 31 | ## Additional Information 32 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | generate_changelog: 12 | runs-on: ubuntu-latest 13 | name: Generate changelog for master branch 14 | steps: 15 | - uses: actions/checkout@v1 16 | 17 | - name: Generate changelog 18 | uses: charmixer/auto-changelog-action@v1 19 | with: 20 | token: ${{secrets.GITHUB_TOKEN}} 21 | 22 | - name: Commit files 23 | env: 24 | CI_USER: jgw96 25 | CI_EMAIL: jgw9617@gmail.com 26 | run: | 27 | git config --local user.email "$CI_EMAIL" 28 | git config --local user.name "$CI_USER" 29 | git add CHANGELOG.md && git commit -m 'Updated CHANGELOG.md' && echo ::set-env name=push::1 || echo "No changes to CHANGELOG.md" 30 | 31 | - name: Push changes 32 | if: env.push == 1 33 | env: 34 | CI_USER: jgw96 35 | CI_TOKEN: ${{secrets.GITHUB_TOKEN}} 36 | run: | 37 | git push "https://$CI_USER:$CI_TOKEN@github.com/$GITHUB_REPOSITORY.git" HEAD:master 38 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | pull_request: 7 | branches: 8 | - master 9 | types: closed 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | 17 | - name: Get version from package.json 18 | uses: nyaayaya/package-version@v1 19 | with: 20 | path: package.json 21 | 22 | 23 | - name: Create Release 24 | id: create_release 25 | uses: actions/create-release@v1 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 28 | with: 29 | tag_name: ${{ env.PACKAGE_VERSION }} 30 | release_name: Release ${{ env.PACKAGE_VERSION }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | build/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.6.9](https://github.com/pwa-builder/pwa-install/tree/1.6.9) (2020-11-05) 4 | 5 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/1.6.8...1.6.9) 6 | 7 | **Closed issues:** 8 | 9 | - Improve focus styling [\#104](https://github.com/pwa-builder/pwa-install/issues/104) 10 | - Use a \ element for div\#installModal? [\#5](https://github.com/pwa-builder/pwa-install/issues/5) 11 | 12 | **Merged pull requests:** 13 | 14 | - pwa-install type declaration files [\#470](https://github.com/pwa-builder/pwa-install/pull/470) ([lee-leonardo](https://github.com/lee-leonardo)) 15 | 16 | ## [1.6.8](https://github.com/pwa-builder/pwa-install/tree/1.6.8) (2020-11-02) 17 | 18 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/1.6.7...1.6.8) 19 | 20 | **Security fixes:** 21 | 22 | - build\(deps\): \[security\] bump node-fetch from 2.6.0 to 2.6.1 [\#463](https://github.com/pwa-builder/pwa-install/pull/463) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 23 | 24 | **Closed issues:** 25 | 26 | - \[Question\] Lack of docs [\#455](https://github.com/pwa-builder/pwa-install/issues/455) 27 | 28 | **Merged pull requests:** 29 | 30 | - Install update [\#467](https://github.com/pwa-builder/pwa-install/pull/467) ([lee-leonardo](https://github.com/lee-leonardo)) 31 | - fix\(\): check out code first [\#466](https://github.com/pwa-builder/pwa-install/pull/466) ([jgw96](https://github.com/jgw96)) 32 | - testing the action [\#465](https://github.com/pwa-builder/pwa-install/pull/465) ([jgw96](https://github.com/jgw96)) 33 | - fix\(\): improve automated release [\#464](https://github.com/pwa-builder/pwa-install/pull/464) ([jgw96](https://github.com/jgw96)) 34 | - build\(deps-dev\): bump @open-wc/testing-helpers from 1.8.9 to 1.8.12 [\#462](https://github.com/pwa-builder/pwa-install/pull/462) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 35 | - build\(deps-dev\): bump @open-wc/testing from 2.5.24 to 2.5.32 [\#461](https://github.com/pwa-builder/pwa-install/pull/461) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 36 | - Strict Mode friendly, examples, start of types [\#460](https://github.com/pwa-builder/pwa-install/pull/460) ([lee-leonardo](https://github.com/lee-leonardo)) 37 | 38 | ## [1.6.7](https://github.com/pwa-builder/pwa-install/tree/1.6.7) (2020-10-28) 39 | 40 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/1.6.4...1.6.7) 41 | 42 | **Security fixes:** 43 | 44 | - build\(deps\): \[security\] bump lodash from 4.17.15 to 4.17.19 [\#356](https://github.com/pwa-builder/pwa-install/pull/356) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 45 | 46 | **Closed issues:** 47 | 48 | - isIOS type [\#457](https://github.com/pwa-builder/pwa-install/issues/457) 49 | - \[Question\] LICENSE [\#454](https://github.com/pwa-builder/pwa-install/issues/454) 50 | - \[Question\] npmignore [\#453](https://github.com/pwa-builder/pwa-install/issues/453) 51 | - \[Question\] What's the best way to "wait" for the install button to appear? [\#434](https://github.com/pwa-builder/pwa-install/issues/434) 52 | - PWABuilder build App not obey the Screen Orientation as per the Manifest defined [\#433](https://github.com/pwa-builder/pwa-install/issues/433) 53 | - Multiple entries from the manifest file are not being processed [\#418](https://github.com/pwa-builder/pwa-install/issues/418) 54 | - Override Component CSS without Shadow Parts [\#406](https://github.com/pwa-builder/pwa-install/issues/406) 55 | - Button doesn't show and serviceworker returns 404 [\#385](https://github.com/pwa-builder/pwa-install/issues/385) 56 | - "Install" not working using pwa-install [\#355](https://github.com/pwa-builder/pwa-install/issues/355) 57 | - Install button doesnt appear on other browsers except chrome [\#345](https://github.com/pwa-builder/pwa-install/issues/345) 58 | - Install button not showing on Firefox [\#342](https://github.com/pwa-builder/pwa-install/issues/342) 59 | - Error 404 pwa-install.js.map [\#293](https://github.com/pwa-builder/pwa-install/issues/293) 60 | - CSS selector not working [\#247](https://github.com/pwa-builder/pwa-install/issues/247) 61 | - Screenshot Carousel - Safari Desktop & Mobile [\#177](https://github.com/pwa-builder/pwa-install/issues/177) 62 | - Support Dark Mode [\#1](https://github.com/pwa-builder/pwa-install/issues/1) 63 | 64 | **Merged pull requests:** 65 | 66 | - v1.6.7 [\#459](https://github.com/pwa-builder/pwa-install/pull/459) ([jgw96](https://github.com/jgw96)) 67 | - build\(deps-dev\): bump typescript from 4.0.2 to 4.0.5 [\#458](https://github.com/pwa-builder/pwa-install/pull/458) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 68 | - Pwabuilder issue 824 [\#404](https://github.com/pwa-builder/pwa-install/pull/404) ([lee-leonardo](https://github.com/lee-leonardo)) 69 | - v1.6.6 [\#403](https://github.com/pwa-builder/pwa-install/pull/403) ([jgw96](https://github.com/jgw96)) 70 | - style\(\): button container is darker in dark mode [\#402](https://github.com/pwa-builder/pwa-install/pull/402) ([jgw96](https://github.com/jgw96)) 71 | - Dark Mode [\#401](https://github.com/pwa-builder/pwa-install/pull/401) ([lee-leonardo](https://github.com/lee-leonardo)) 72 | - chore\(\): update packages [\#400](https://github.com/pwa-builder/pwa-install/pull/400) ([jgw96](https://github.com/jgw96)) 73 | - build\(deps-dev\): bump rollup from 2.23.0 to 2.26.5 [\#399](https://github.com/pwa-builder/pwa-install/pull/399) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 74 | - v1.6.5 [\#379](https://github.com/pwa-builder/pwa-install/pull/379) ([jgw96](https://github.com/jgw96)) 75 | - Source map [\#378](https://github.com/pwa-builder/pwa-install/pull/378) ([jgw96](https://github.com/jgw96)) 76 | - chore\(\): update packages [\#377](https://github.com/pwa-builder/pwa-install/pull/377) ([jgw96](https://github.com/jgw96)) 77 | - use this contrast ratio color [\#373](https://github.com/pwa-builder/pwa-install/pull/373) ([lee-leonardo](https://github.com/lee-leonardo)) 78 | - chore\(\): update image [\#370](https://github.com/pwa-builder/pwa-install/pull/370) ([jgw96](https://github.com/jgw96)) 79 | - build\(deps-dev\): bump @open-wc/testing-karma from 3.4.7 to 4.0.1 [\#369](https://github.com/pwa-builder/pwa-install/pull/369) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 80 | - build\(deps-dev\): bump es-dev-server from 1.56.1 to 1.57.1 [\#368](https://github.com/pwa-builder/pwa-install/pull/368) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 81 | - build\(deps-dev\): bump @open-wc/karma-esm from 2.16.17 to 3.0.1 [\#367](https://github.com/pwa-builder/pwa-install/pull/367) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 82 | - build\(deps-dev\): bump karma from 5.1.0 to 5.1.1 [\#366](https://github.com/pwa-builder/pwa-install/pull/366) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 83 | - Pwabuilder issue 861 & 859 [\#364](https://github.com/pwa-builder/pwa-install/pull/364) ([lee-leonardo](https://github.com/lee-leonardo)) 84 | - build\(deps-dev\): bump rollup from 2.22.1 to 2.23.0 [\#360](https://github.com/pwa-builder/pwa-install/pull/360) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 85 | - build\(deps-dev\): bump rollup from 2.21.0 to 2.22.1 [\#358](https://github.com/pwa-builder/pwa-install/pull/358) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 86 | - build\(deps-dev\): bump typescript from 3.9.5 to 3.9.7 [\#357](https://github.com/pwa-builder/pwa-install/pull/357) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 87 | - build\(deps-dev\): bump @open-wc/testing-karma from 3.4.4 to 3.4.7 [\#354](https://github.com/pwa-builder/pwa-install/pull/354) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 88 | - build\(deps-dev\): bump es-dev-server from 1.55.0 to 1.56.1 [\#353](https://github.com/pwa-builder/pwa-install/pull/353) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 89 | - build\(deps-dev\): bump @open-wc/karma-esm from 2.16.14 to 2.16.17 [\#352](https://github.com/pwa-builder/pwa-install/pull/352) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 90 | - build\(deps-dev\): bump @rollup/plugin-strip from 1.3.3 to 2.0.0 [\#351](https://github.com/pwa-builder/pwa-install/pull/351) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 91 | - build\(deps-dev\): bump @open-wc/testing-helpers from 1.8.3 to 1.8.4 [\#350](https://github.com/pwa-builder/pwa-install/pull/350) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 92 | - build\(deps-dev\): bump @open-wc/testing from 2.5.18 to 2.5.19 [\#349](https://github.com/pwa-builder/pwa-install/pull/349) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 93 | - build\(deps-dev\): bump rollup from 2.16.1 to 2.21.0 [\#348](https://github.com/pwa-builder/pwa-install/pull/348) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 94 | 95 | ## [1.6.4](https://github.com/pwa-builder/pwa-install/tree/1.6.4) (2020-06-16) 96 | 97 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/1.6.3...1.6.4) 98 | 99 | **Closed issues:** 100 | 101 | - \[FEATURE\] Add I18n for pwa install [\#274](https://github.com/pwa-builder/pwa-install/issues/274) 102 | 103 | **Merged pull requests:** 104 | 105 | - chore\(\): update deps [\#331](https://github.com/pwa-builder/pwa-install/pull/331) ([jgw96](https://github.com/jgw96)) 106 | - build\(deps-dev\): bump es-dev-server from 1.46.5 to 1.55.0 [\#324](https://github.com/pwa-builder/pwa-install/pull/324) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 107 | - build\(deps-dev\): bump typescript from 3.8.3 to 3.9.5 [\#320](https://github.com/pwa-builder/pwa-install/pull/320) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 108 | - build\(deps-dev\): bump rollup-plugin-terser from 5.3.0 to 6.1.0 [\#316](https://github.com/pwa-builder/pwa-install/pull/316) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 109 | - build\(deps-dev\): bump rollup from 2.7.1 to 2.11.2 [\#315](https://github.com/pwa-builder/pwa-install/pull/315) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 110 | - build\(deps-dev\): bump @rollup/plugin-strip from 1.3.2 to 1.3.3 [\#283](https://github.com/pwa-builder/pwa-install/pull/283) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 111 | - build\(deps-dev\): bump concurrently from 5.1.0 to 5.2.0 [\#261](https://github.com/pwa-builder/pwa-install/pull/261) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 112 | 113 | ## [1.6.3](https://github.com/pwa-builder/pwa-install/tree/1.6.3) (2020-04-27) 114 | 115 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/1.6.2...1.6.3) 116 | 117 | **Closed issues:** 118 | 119 | - Install button not showing [\#258](https://github.com/pwa-builder/pwa-install/issues/258) 120 | 121 | **Merged pull requests:** 122 | 123 | - fix\(\): set max width on modal [\#266](https://github.com/pwa-builder/pwa-install/pull/266) ([jgw96](https://github.com/jgw96)) 124 | - build\(deps-dev\): bump @open-wc/karma-esm from 2.13.22 to 2.13.26 [\#257](https://github.com/pwa-builder/pwa-install/pull/257) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 125 | - build\(deps-dev\): bump es-dev-server from 1.45.3 to 1.46.5 [\#256](https://github.com/pwa-builder/pwa-install/pull/256) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 126 | - build\(deps-dev\): bump @open-wc/testing from 2.5.10 to 2.5.13 [\#255](https://github.com/pwa-builder/pwa-install/pull/255) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 127 | - build\(deps-dev\): bump rollup from 2.3.3 to 2.7.1 [\#254](https://github.com/pwa-builder/pwa-install/pull/254) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 128 | - build\(deps-dev\): bump @open-wc/testing-karma from 3.3.11 to 3.3.15 [\#253](https://github.com/pwa-builder/pwa-install/pull/253) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 129 | - build\(deps-dev\): bump @open-wc/testing-helpers from 1.7.0 to 1.7.2 [\#249](https://github.com/pwa-builder/pwa-install/pull/249) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 130 | 131 | ## [1.6.2](https://github.com/pwa-builder/pwa-install/tree/1.6.2) (2020-04-20) 132 | 133 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/1.6.1...1.6.2) 134 | 135 | **Closed issues:** 136 | 137 | - \[Question\] styling with CSS variables [\#245](https://github.com/pwa-builder/pwa-install/issues/245) 138 | 139 | **Merged pull requests:** 140 | 141 | - Styling fix [\#246](https://github.com/pwa-builder/pwa-install/pull/246) ([jgw96](https://github.com/jgw96)) 142 | - Safari - Carousel Issue \#177 [\#242](https://github.com/pwa-builder/pwa-install/pull/242) ([lee-leonardo](https://github.com/lee-leonardo)) 143 | 144 | ## [1.6.1](https://github.com/pwa-builder/pwa-install/tree/1.6.1) (2020-04-14) 145 | 146 | [Full Changelog](https://github.com/pwa-builder/pwa-install/compare/81c0c5577e05f6f4cc6b435c75cb031d3a20ab62...1.6.1) 147 | 148 | **Security fixes:** 149 | 150 | - build\(deps\): \[security\] bump acorn from 7.1.0 to 7.1.1 [\#204](https://github.com/pwa-builder/pwa-install/pull/204) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 151 | 152 | **Closed issues:** 153 | 154 | - use getInstalledRelatedApps api [\#235](https://github.com/pwa-builder/pwa-install/issues/235) 155 | - \[Question\]Install button Missing [\#232](https://github.com/pwa-builder/pwa-install/issues/232) 156 | - Install button should not disappear on cancel [\#224](https://github.com/pwa-builder/pwa-install/issues/224) 157 | - Call to action is below the fold on iOS [\#208](https://github.com/pwa-builder/pwa-install/issues/208) 158 | - \[Question\] Test [\#182](https://github.com/pwa-builder/pwa-install/issues/182) 159 | - Using with React + TypeScript [\#172](https://github.com/pwa-builder/pwa-install/issues/172) 160 | - \[Idea\] Show rating and reviews from Microsoft Store [\#162](https://github.com/pwa-builder/pwa-install/issues/162) 161 | - \[Feature Request\] Add a method to check if the pwa is already installed [\#161](https://github.com/pwa-builder/pwa-install/issues/161) 162 | - Design issue on certain devices [\#142](https://github.com/pwa-builder/pwa-install/issues/142) 163 | - Can't see third screenshot on desktop [\#137](https://github.com/pwa-builder/pwa-install/issues/137) 164 | - Browser is not a valid display type [\#136](https://github.com/pwa-builder/pwa-install/issues/136) 165 | - "Request scheme 'chrome-extension' is unsupported" occasionally appearing in debug console [\#135](https://github.com/pwa-builder/pwa-install/issues/135) 166 | - Service Worker check complains about lack of pushManager registration with no remediation [\#134](https://github.com/pwa-builder/pwa-install/issues/134) 167 | - Overflow with enough content or certain screen [\#112](https://github.com/pwa-builder/pwa-install/issues/112) 168 | - Page still scrollable while modal is up [\#103](https://github.com/pwa-builder/pwa-install/issues/103) 169 | - User Agent sniffing fails to take into account iPadOS [\#86](https://github.com/pwa-builder/pwa-install/issues/86) 170 | - Issue on non 3:2 13 inch displays [\#80](https://github.com/pwa-builder/pwa-install/issues/80) 171 | - Does not always catch the deferredPrompt event [\#65](https://github.com/pwa-builder/pwa-install/issues/65) 172 | - Replace current screenshot carousel with simpler / smaller implementation [\#64](https://github.com/pwa-builder/pwa-install/issues/64) 173 | - show/hide events [\#36](https://github.com/pwa-builder/pwa-install/issues/36) 174 | - Should hide after install [\#34](https://github.com/pwa-builder/pwa-install/issues/34) 175 | - Layout on iPhone 5SE [\#33](https://github.com/pwa-builder/pwa-install/issues/33) 176 | - Modal footer is messed up at a certain size [\#32](https://github.com/pwa-builder/pwa-install/issues/32) 177 | - new method: closePrompt\(\) [\#31](https://github.com/pwa-builder/pwa-install/issues/31) 178 | - Localization: install button text as a property [\#25](https://github.com/pwa-builder/pwa-install/issues/25) 179 | - Unit Tests [\#21](https://github.com/pwa-builder/pwa-install/issues/21) 180 | - Minify build output [\#17](https://github.com/pwa-builder/pwa-install/issues/17) 181 | - Can't fire openPrompt function [\#15](https://github.com/pwa-builder/pwa-install/issues/15) 182 | - \[Feature\] showeligible attribute [\#14](https://github.com/pwa-builder/pwa-install/issues/14) 183 | - Support localization [\#13](https://github.com/pwa-builder/pwa-install/issues/13) 184 | - Uncaught TypeError [\#11](https://github.com/pwa-builder/pwa-install/issues/11) 185 | - Close X doesn't listen to --modal-z-index \(css-variable\) [\#10](https://github.com/pwa-builder/pwa-install/issues/10) 186 | - Misplaced close X [\#9](https://github.com/pwa-builder/pwa-install/issues/9) 187 | - Images don't display on Safari [\#8](https://github.com/pwa-builder/pwa-install/issues/8) 188 | - Research improvements around the Safari experience [\#7](https://github.com/pwa-builder/pwa-install/issues/7) 189 | - Bad experience when beforeinstallprompt hasn't fired yet [\#6](https://github.com/pwa-builder/pwa-install/issues/6) 190 | - Display the start\_url [\#2](https://github.com/pwa-builder/pwa-install/issues/2) 191 | 192 | **Merged pull requests:** 193 | 194 | - Update README.md [\#225](https://github.com/pwa-builder/pwa-install/pull/225) ([richardtaylordawson](https://github.com/richardtaylordawson)) 195 | - build\(deps-dev\): bump @open-wc/karma-esm from 2.13.18 to 2.13.20 [\#219](https://github.com/pwa-builder/pwa-install/pull/219) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 196 | - build\(deps-dev\): bump @open-wc/testing-karma from 3.1.54 to 3.3.9 [\#218](https://github.com/pwa-builder/pwa-install/pull/218) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 197 | - build\(deps-dev\): bump @open-wc/testing from 2.5.6 to 2.5.8 [\#216](https://github.com/pwa-builder/pwa-install/pull/216) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 198 | - build\(deps\): bump lit-element from 2.2.1 to 2.3.1 [\#213](https://github.com/pwa-builder/pwa-install/pull/213) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 199 | - build\(deps-dev\): bump @open-wc/testing-helpers from 1.6.0 to 1.6.2 [\#212](https://github.com/pwa-builder/pwa-install/pull/212) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 200 | - build\(deps-dev\): bump es-dev-server from 1.36.1 to 1.45.1 [\#207](https://github.com/pwa-builder/pwa-install/pull/207) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 201 | - build\(deps-dev\): bump @open-wc/karma-esm from 2.10.3 to 2.13.18 [\#205](https://github.com/pwa-builder/pwa-install/pull/205) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 202 | - build\(deps-dev\): bump @open-wc/testing-helpers from 1.3.0 to 1.6.0 [\#200](https://github.com/pwa-builder/pwa-install/pull/200) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 203 | - build\(deps-dev\): bump @open-wc/testing from 2.3.9 to 2.5.6 [\#196](https://github.com/pwa-builder/pwa-install/pull/196) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 204 | - build\(deps-dev\): bump rollup from 1.29.1 to 1.32.1 [\#193](https://github.com/pwa-builder/pwa-install/pull/193) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 205 | - build\(deps-dev\): bump typescript from 3.7.5 to 3.8.3 [\#187](https://github.com/pwa-builder/pwa-install/pull/187) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 206 | - fix\(\): never show if already installed [\#183](https://github.com/pwa-builder/pwa-install/pull/183) ([jgw96](https://github.com/jgw96)) 207 | - Intro-Question-Template [\#181](https://github.com/pwa-builder/pwa-install/pull/181) ([Kyaa-dost](https://github.com/Kyaa-dost)) 208 | - Update bug\_report.md [\#175](https://github.com/pwa-builder/pwa-install/pull/175) ([Kyaa-dost](https://github.com/Kyaa-dost)) 209 | - Update README.md [\#174](https://github.com/pwa-builder/pwa-install/pull/174) ([richardtaylordawson](https://github.com/richardtaylordawson)) 210 | - build\(deps-dev\): bump rollup-plugin-minify-html-literals from 1.2.2 to 1.2.3 [\#163](https://github.com/pwa-builder/pwa-install/pull/163) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 211 | - build\(deps-dev\): bump @rollup/plugin-strip from 1.3.1 to 1.3.2 [\#149](https://github.com/pwa-builder/pwa-install/pull/149) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 212 | - build\(deps-dev\): bump concurrently from 5.0.2 to 5.1.0 [\#147](https://github.com/pwa-builder/pwa-install/pull/147) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 213 | - build\(deps-dev\): bump rollup from 1.29.0 to 1.29.1 [\#141](https://github.com/pwa-builder/pwa-install/pull/141) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 214 | - build\(deps-dev\): bump typescript from 3.7.4 to 3.7.5 [\#138](https://github.com/pwa-builder/pwa-install/pull/138) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 215 | - build\(deps-dev\): bump rollup from 1.28.0 to 1.29.0 [\#130](https://github.com/pwa-builder/pwa-install/pull/130) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 216 | - build\(deps-dev\): bump es-dev-server from 1.36.0 to 1.36.1 [\#126](https://github.com/pwa-builder/pwa-install/pull/126) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 217 | - build\(deps-dev\): bump es-dev-server from 1.26.2 to 1.36.0 [\#123](https://github.com/pwa-builder/pwa-install/pull/123) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 218 | - feat\(pwa-install\): minor tweak to prevent ESC hatch override [\#120](https://github.com/pwa-builder/pwa-install/pull/120) ([peterblazejewicz](https://github.com/peterblazejewicz)) 219 | - More Bugs [\#119](https://github.com/pwa-builder/pwa-install/pull/119) ([hipstersmoothie](https://github.com/hipstersmoothie)) 220 | - build\(deps-dev\): bump rollup from 1.27.3 to 1.28.0 [\#115](https://github.com/pwa-builder/pwa-install/pull/115) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 221 | - fix overflow scroll and cut off text [\#114](https://github.com/pwa-builder/pwa-install/pull/114) ([hipstersmoothie](https://github.com/hipstersmoothie)) 222 | - center title on mobile [\#113](https://github.com/pwa-builder/pwa-install/pull/113) ([hipstersmoothie](https://github.com/hipstersmoothie)) 223 | - fix close button placement on certain screen sizes [\#111](https://github.com/pwa-builder/pwa-install/pull/111) ([hipstersmoothie](https://github.com/hipstersmoothie)) 224 | - allow for white-space in description [\#110](https://github.com/pwa-builder/pwa-install/pull/110) ([hipstersmoothie](https://github.com/hipstersmoothie)) 225 | - correct word in default explainer [\#109](https://github.com/pwa-builder/pwa-install/pull/109) ([hipstersmoothie](https://github.com/hipstersmoothie)) 226 | - Fix long features list text clipping [\#108](https://github.com/pwa-builder/pwa-install/pull/108) ([hipstersmoothie](https://github.com/hipstersmoothie)) 227 | - add docs for explainer text [\#107](https://github.com/pwa-builder/pwa-install/pull/107) ([hipstersmoothie](https://github.com/hipstersmoothie)) 228 | - use flexbox for saner styling of modal footer [\#106](https://github.com/pwa-builder/pwa-install/pull/106) ([hipstersmoothie](https://github.com/hipstersmoothie)) 229 | - add focus styles to interactable elements [\#105](https://github.com/pwa-builder/pwa-install/pull/105) ([hipstersmoothie](https://github.com/hipstersmoothie)) 230 | - build\(deps-dev\): bump typescript from 3.7.2 to 3.7.4 [\#97](https://github.com/pwa-builder/pwa-install/pull/97) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 231 | - build\(deps-dev\): bump concurrently from 5.0.0 to 5.0.2 [\#92](https://github.com/pwa-builder/pwa-install/pull/92) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 232 | - PULL\_REQUEST\_TEMPLATE.md [\#75](https://github.com/pwa-builder/pwa-install/pull/75) ([Kyaa-dost](https://github.com/Kyaa-dost)) 233 | - build\(deps-dev\): bump es-dev-server from 1.25.1 to 1.26.2 [\#55](https://github.com/pwa-builder/pwa-install/pull/55) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 234 | - build\(deps-dev\): bump @open-wc/karma-esm from 2.9.8 to 2.10.3 [\#44](https://github.com/pwa-builder/pwa-install/pull/44) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 235 | - build\(deps-dev\): bump rollup from 1.27.2 to 1.27.3 [\#43](https://github.com/pwa-builder/pwa-install/pull/43) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 236 | - build\(deps-dev\): bump es-dev-server from 1.23.1 to 1.25.1 [\#42](https://github.com/pwa-builder/pwa-install/pull/42) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 237 | - feat\(\): add iOS support [\#37](https://github.com/pwa-builder/pwa-install/pull/37) ([jgw96](https://github.com/jgw96)) 238 | - build\(deps-dev\): bump rollup from 1.27.0 to 1.27.2 [\#35](https://github.com/pwa-builder/pwa-install/pull/35) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 239 | - chore\(\): karma does not fail when global thresholds not met [\#29](https://github.com/pwa-builder/pwa-install/pull/29) ([jgw96](https://github.com/jgw96)) 240 | - Unit test [\#28](https://github.com/pwa-builder/pwa-install/pull/28) ([jgw96](https://github.com/jgw96)) 241 | - Changed Install/Cancel button texts to properties [\#27](https://github.com/pwa-builder/pwa-install/pull/27) ([J0ne](https://github.com/J0ne)) 242 | - feat\(\): add showeligible attribute [\#26](https://github.com/pwa-builder/pwa-install/pull/26) ([benjibuiltit](https://github.com/benjibuiltit)) 243 | - build\(deps-dev\): bump typescript from 3.6.4 to 3.7.2 [\#24](https://github.com/pwa-builder/pwa-install/pull/24) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 244 | - build\(deps-dev\): bump es-dev-server from 1.18.4 to 1.23.1 [\#23](https://github.com/pwa-builder/pwa-install/pull/23) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 245 | - build\(deps-dev\): bump rollup from 1.26.0 to 1.27.0 [\#22](https://github.com/pwa-builder/pwa-install/pull/22) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) 246 | - v0.9.8 [\#20](https://github.com/pwa-builder/pwa-install/pull/20) ([jgw96](https://github.com/jgw96)) 247 | - build\(\): minify production build [\#19](https://github.com/pwa-builder/pwa-install/pull/19) ([jgw96](https://github.com/jgw96)) 248 | - feat\(\): first iteration of iOS experience [\#18](https://github.com/pwa-builder/pwa-install/pull/18) ([jgw96](https://github.com/jgw96)) 249 | - PR for Support localization \#13 [\#16](https://github.com/pwa-builder/pwa-install/pull/16) ([J0ne](https://github.com/J0ne)) 250 | - feat\(\): display install explainer [\#3](https://github.com/pwa-builder/pwa-install/pull/3) ([jgw96](https://github.com/jgw96)) 251 | 252 | 253 | 254 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 255 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the pwa-install component 2 | 3 | The pwa-install component is a [web component](https://developer.mozilla.org/en-US/docs/Web/Web_Components) built using [lit-element](https://lit-element.polymer-project.org/). If you are not familiar with lit-element, we recommend reading through the [lit-element guide](https://lit-element.polymer-project.org/guide) before contributing. 4 | 5 | ## Get started developing 6 | 7 | ### Prerequisites 8 | 9 | At a minimum, you will need the following things properly installed on your computer to get started: 10 | 11 | * [Git](http://git-scm.com/) 12 | * [Node.js](http://nodejs.org/) (with NPM) 13 | * [NPM](http://npmjs.com/) 14 | 15 | ### Installation 16 | 17 | * `git clone https://github.com/pwa-builder/pwa-install.git` 18 | * navigate inside the new directory 19 | * `npm install` 20 | 21 | ### Developing 22 | 23 | * `npm run dev` 24 | * Your browser will open `www/index.html` with a livereload dev server. 25 | 26 | Once you have run `npm run dev` you can then start making your changes to `src/pwa-install.ts`. Every time you save your work the code will be re-compiled and the page will reload with your new code. 27 | 28 | 29 | # Frequently Asked Questions 30 | 31 | ## Who can contribute? 32 | 33 | Anyone who wants to be part of bringing PWAs to the web can contribute to pwa-install! Skill level doesn't matter. All contributions big and small help us out. We have reviewers who can help you through the process the first few times. 34 | 35 | ## Why does Microsoft need contributors? 36 | 37 | pwa-install was founded by Microsoft, but it's only moved forward because of the community's help. We open-sourced this because Microsoft wants to support the cross-platform development ecosystem and work with the community and browsers to direct the future of PWAs. 38 | 39 | ## What kind of contributions is pwa-install seeking? 40 | 41 | No contribution is too small or too big. Bigger tasks take longer to review while smaller ones get feedback more quickly. Most contributors start with a small update, a bug fix, or docs improvement, and then move on to bigger tasks as they gain more familiarity with the component. 42 | 43 | ## What should I check before submitting a pull request? 44 | 45 | For every contribution, you must: 46 | 47 | * Test: run `npm run test` and ensure that all tests pass. 48 | * Target master branch (or an appropriate release branch if appropriate for a bug fix) 49 | * Ensure that your contribution follows [standard accessibility guidelines](https://docs.microsoft.com/en-us/microsoft-edge/accessibility/design). Use tools like https://webhint.io/ to validate your changes. 50 | 51 | If adding a new feature, 52 | **you should open an issue with us** on the main pwa-install repo on Github (https://github.com/pwa-builder/pwa-install) before you start coding. 53 | 54 | Once you have opened your PR, feel free to add a reviewer (Github should recommend people on the team). When the PR has been reviewed and is good to go, a team member will merge it. 55 | 56 | ## After my pull request gets merged, when does it become part of a pwa-install release? 57 | 58 | We will usually add fixes in the next planned release, but sometimes it makes sense to add a contribution in a later update to ensure the quality and performance of the next release. 59 | 60 | ## Found bugs? 61 | If you find a bug, you can help us by 62 | [submitting an issue](https://github.com/pwa-builder/pwa-install/issues). Even better, you can 63 | [submit a Pull Request](#pr) with a fix. 64 | 65 | ## Special thanks 66 | This contributing doc was inspired by our friends' version at the [Windows Community Toolkit](https://github.com/windows-toolkit/WindowsCommunityToolkit/). 67 | thank you! 68 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ManifoldJS 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The code for this component has moved to the main PWABuilder monorepo and this repository has been archived. Please visit our [monorepo for the latest source](https://github.com/pwa-builder/pwabuilder). 2 | 3 | # pwa-install 4 | 5 | ![CI](https://github.com/pwa-builder/pwa-install/workflows/CI/badge.svg) 6 | 7 | Please use our [main repository for any issues/bugs/features suggestion](https://github.com/pwa-builder/PWABuilder/issues/new/choose). 8 | 9 | `pwa-install` is a [web component](https://meowni.ca/posts/web-components-with-otters/) from the [PWABuilder](https://pwabuilder.com) team that brings an awesome "install" experience to your Progressive Web App! 10 | 11 | _Built with [lit-element](https://lit-element.polymer-project.org/)_ 12 | 13 | ### What does it look like? 14 | 15 | An image of what the component looks like 16 | 17 | ## Supported Browsers 18 | 19 | - Edge 20 | - Chrome 21 | - Firefox 22 | - Safari 23 | 24 | ## Using this component 25 | 26 | ## Install 27 | 28 | There are two ways to use this component. For simple projects or just to get started fast, we recommend using the component by script tag. If your project is using [npm](https://www.npmjs.com/) then we recommend using the npm package. 29 | 30 | ### Script tag 31 | 32 | - Put this script tag in the head of your index.html: 33 | 34 | ```html 35 | 39 | ``` 40 | 41 | ### NPM 42 | 43 | - Run `npm install @pwabuilder/pwainstall` 44 | - import with `import '@pwabuilder/pwainstall'` 45 | 46 | Then you can use the element `` anywhere in your template, JSX, html etc. 47 | live demo: https://pwainstall.glitch.me 48 | 49 | ## API 50 | 51 | ### Properties 52 | 53 | | Property | Attribute | Description | Type | Default | 54 | | -------------------- | -------------------- | ------------------------------------------------------------------------------- | --------- | --------------------------------------------------- | 55 | | `openmodal` | `openmodal` | Controls the opening of the modal via attribute, consider using the function | `boolean` | `false` | 56 | | `usecustom` | `usecustom` | Hides default button | `boolean` | `false` | 57 | | `manifestpath` | `manifestpath` | path to your web manifest | `string` | `manifest.json` | 58 | | `explainer` | `explainer` | Controls the text of the explainer text just below the title of the app header | `string` | `This app can be installed on` | 59 | | `featuresheader` | `featuresheader` | Controls the text of the features header | `string` | `Key Features` | 60 | | `descriptionheader` | `descriptionheader` | Controls the text of the description header | `string` | `Description` | 61 | | `installbuttontext` | `installbuttontext` | Controls the text of the install button | `string` | `Install` | 62 | | `cancelbuttontext` | `cancelbuttontext` | Controls the text of the cancel button | `string` | `Cancel` | 63 | | `iosinstallinfotext` | `iosinstallinfotext` | Controls the iOS installation info text | `string` | `Tap the share button and then 'Add to Homescreen'` | 64 | 65 | ### Methods 66 | 67 | | name | Description | 68 | | ---------------------- | ------------------------------------- | 69 | | `openPrompt()` | `Opens the install modal` | 70 | | `closePrompt()` | `Closes the install modal` | 71 | | `getInstalledStatus()` | `Tell if the PWA is installed or not` | 72 | 73 | Interactions with the methods requires a reference to the element itself, if using webcomponents or a library like Lit-Element or Fast-Element, this can be done easily within the if using the component from the browser 74 | 75 | ## Styling 76 | 77 | ### CSS Variables 78 | 79 | We recommend using our [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) to easliy tweak the style of this component to fit your project. Here are our current 80 | supported CSS variables. 81 | 82 | | name | Description | 83 | | -------------------------- | ----------------------------------------------------- | 84 | | `--install-button-color` | `Changes the color of the install button` | 85 | | `--modal-z-index` | `Changes the z-index of the install modal` | 86 | | `--modal-background-index` | `Changes the z-index of the install modal background` | 87 | | `--modal-background-color` | `Changes the background color of the install modal` | 88 | 89 | ### Shadow Parts 90 | 91 | If you need to style this component more comprehensively, you can use [Shadow Parts](https://dev.to/webpadawan/css-shadow-parts-are-coming-mi5) to style both the install button and the install modal. To target these two elements you can use `pwa-install::part(openButton)` and `pwa-install::part(installModal)` respectively. For example, to make the background of the install button grey, I would need this CSS: 92 | 93 | ```css 94 | pwa-install::part(openButton) { 95 | background: grey; 96 | } 97 | ``` 98 | 99 | ## Advanced Examples 100 | 101 | #### Usage with typescript 102 | 103 | ```typescript 104 | 105 | import "@pwabuilder/pwainstall"; // module import, allows for use in templates. 106 | 107 | class YourClass extends RenderLib { 108 | ... 109 | 110 | get installComponent(): PWAInstallComponent { 111 | return this.shadowRoot?.querySelector("pwa-install"); 112 | } 113 | 114 | ... 115 | } 116 | 117 | ``` 118 | 119 | #### Example with Component Reference (Web Components) 120 | 121 | ```html 122 | 127 | ``` 128 | 129 | ```javascript 130 | customElements.define( 131 | "example", 132 | class Example extends HTMLElement { 133 | constructor() { 134 | super(); 135 | let template = document.getElementById("example"); 136 | let templateContent = template.content; 137 | this.shadowRoot = this.attachShadow({ mode: "open" }).appendChild( 138 | templateContent.cloneNode(true) 139 | ); 140 | this.installComponent = document 141 | .getElementsByTagName("el-example")[0] 142 | .shadowRoot.querySelector("pwa-install"); 143 | } 144 | } 145 | ); 146 | ``` 147 | 148 | #### Example with Component Reference (Lit-Element) 149 | 150 | ```javascript 151 | // Using module import 152 | import { LitElement, html, property, query } from "lit-element"; 153 | import "@pwabuilder/pwainstall"; 154 | 155 | class Example extends LitElement { 156 | @query("installId") componentRef: HTMLElement; 157 | 158 | render() { 159 | return html` 160 | 161 | 162 | 163 | `; 164 | } 165 | 166 | interactionWithComponent() { 167 | // See the methods section 168 | this.componentRef.getInstalledStatus(); 169 | } 170 | } 171 | ``` 172 | 173 | #### Example with Component Reference (Fast-Element) 174 | 175 | ```javascript 176 | const template = html` 177 | 178 | ` 179 | 180 | @customElement({ ... }) 181 | class Example extends FASTElement { 182 | @observable installComponent: PWAInstallComponent | undefined; 183 | 184 | @volatile 185 | get installComponent() { 186 | return this.shadowRoot.querySelector("pwa-install"); 187 | } 188 | 189 | interactionWithComponent() { 190 | this.installComponent.getInstalledStatus(); 191 | } 192 | } 193 | 194 | ``` 195 | 196 | ### Example of grabbing from the dom 197 | 198 | ```html 199 | 200 | 204 | 205 | 206 | 207 | 211 | 212 | ``` 213 | 214 | ### Example of programmatically creating the element 215 | 216 | ```html 217 | 218 | 222 | 223 | 224 | 229 | 230 | ``` 231 | -------------------------------------------------------------------------------- /assets/installsnip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwa-builder/pwa-install/9b2895a539b3bedb520b23378c3a556e78f24e82/assets/installsnip.png -------------------------------------------------------------------------------- /build/pwa-install.d.ts: -------------------------------------------------------------------------------- 1 | import { LitElement } from "lit-element"; 2 | export interface PWAInstallAttributes { 3 | openmodal?: boolean; 4 | usecustom?: boolean; 5 | manifestpath?: string; 6 | explainer?: string; 7 | featuresheader?: string; 8 | descriptionheader?: string; 9 | installbuttontext?: string; 10 | cancelbuttontext?: string; 11 | iosinstallinfotext?: string; 12 | } 13 | export interface PWAInstallMethods { 14 | openPrompt(): void; 15 | closePrompt(): void; 16 | getInstalledStatus(): boolean; 17 | } 18 | export declare type PWAInstall = PWAInstallAttributes & PWAInstallMethods; 19 | interface ManifestData { 20 | name: string; 21 | short_name: string; 22 | description: string; 23 | screenshots: Array; 24 | features: Array; 25 | icons: Array; 26 | } 27 | export declare class pwainstall extends LitElement implements PWAInstall { 28 | manifestpath: string; 29 | iconpath: string; 30 | manifestdata: ManifestData; 31 | openmodal: boolean; 32 | isSupportingBrowser: boolean; 33 | isIOS: boolean; 34 | installed: boolean; 35 | hasprompt: boolean; 36 | usecustom: boolean; 37 | relatedApps: any[]; 38 | explainer: string; 39 | featuresheader: string; 40 | descriptionheader: string; 41 | installbuttontext: string; 42 | cancelbuttontext: string; 43 | iosinstallinfotext: string; 44 | deferredprompt: any; 45 | static get styles(): import("lit-element").CSSResult; 46 | constructor(); 47 | firstUpdated(): Promise; 48 | handleInstallPromptEvent(event: Event): void; 49 | checkManifest(manifestData: ManifestData): void; 50 | getManifestData(): Promise; 51 | scrollToLeft(): void; 52 | scrollToRight(): void; 53 | openPrompt(): void; 54 | closePrompt(): void; 55 | shouldShowInstall(): boolean; 56 | install(): Promise; 57 | getInstalledStatus(): boolean; 58 | cancel(): Promise; 59 | focusOut(): void; 60 | render(): import("lit-element").TemplateResult; 61 | } 62 | export {}; 63 | -------------------------------------------------------------------------------- /build/pwa-install.js: -------------------------------------------------------------------------------- 1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 5 | return c > 3 && r && Object.defineProperty(target, key, r), r; 6 | }; 7 | import { LitElement, html, customElement, property, css } from "lit-element"; 8 | let pwainstall = class pwainstall extends LitElement { 9 | constructor() { 10 | super(); 11 | this.manifestpath = "manifest.json"; 12 | this.iconpath = ""; 13 | this.manifestdata = { 14 | name: "", 15 | short_name: "", 16 | description: "", 17 | icons: [], 18 | screenshots: [], 19 | features: [], 20 | }; 21 | this.openmodal = false; 22 | this.hasprompt = false; 23 | this.usecustom = false; 24 | this.relatedApps = []; 25 | this.explainer = "This app can be installed on your PC or mobile device. This will allow this web app to look and behave like any other installed app. You will find it in your app lists and be able to pin it to your home screen, start menus or task bars. This installed web app will also be able to safely interact with other apps and your operating system. "; 26 | this.featuresheader = "Key Features"; 27 | this.descriptionheader = "Description"; 28 | this.installbuttontext = "Install"; 29 | this.cancelbuttontext = "Cancel"; 30 | this.iosinstallinfotext = "Tap the share button and then 'Add to Homescreen'"; 31 | // check for beforeinstallprompt support 32 | this.isSupportingBrowser = window.hasOwnProperty("BeforeInstallPromptEvent"); 33 | // handle iOS specifically 34 | // this includes the regular iPad 35 | // and the iPad pro 36 | // but not macOS 37 | this.isIOS = 38 | navigator.userAgent.includes("iPhone") || 39 | navigator.userAgent.includes("iPad") || 40 | (navigator.userAgent.includes("Macintosh") && 41 | typeof navigator.maxTouchPoints === "number" && 42 | navigator.maxTouchPoints > 2); 43 | this.installed = false; 44 | // grab an install event 45 | window.addEventListener("beforeinstallprompt", (event) => this.handleInstallPromptEvent(event)); 46 | document.addEventListener("keyup", (event) => { 47 | if (event.key === "Escape") { 48 | this.cancel(); 49 | } 50 | }); 51 | } 52 | static get styles() { 53 | return css ` 54 | :host { 55 | --install-focus-color: #919c9c; 56 | --install-button-color: #0078d4; 57 | --modal-z-index: 9999; 58 | --background-z-index: 9998; 59 | --modal-background-color: white; 60 | } 61 | 62 | button { 63 | outline: none; 64 | } 65 | 66 | #installModalWrapper { 67 | height: 100vh; 68 | width: 100vw; 69 | overflow: auto; 70 | position: fixed; 71 | bottom: 0; 72 | top: 0; 73 | left: 0; 74 | right: 0; 75 | z-index: var(--modal-z-index); 76 | 77 | display: flex; 78 | justify-content: center; 79 | align-items: center; 80 | } 81 | 82 | #descriptionWrapper { 83 | margin-bottom: 3em; 84 | } 85 | 86 | #installModal { 87 | position: absolute; 88 | background: var(--modal-background-color); 89 | font-family: sans-serif; 90 | box-shadow: 0px 25px 26px rgba(32, 36, 50, 0.25), 91 | 0px 5px 9px rgba(51, 58, 83, 0.53); 92 | border-radius: 10px; 93 | display: flex; 94 | flex-direction: column; 95 | padding: 0; 96 | 97 | animation-name: opened; 98 | animation-duration: 150ms; 99 | 100 | z-index: var(--modal-z-index); 101 | 102 | max-width: 56em; 103 | } 104 | 105 | @keyframes opened { 106 | from { 107 | transform: scale(0.8, 0.8); 108 | opacity: 0.4; 109 | } 110 | to { 111 | transform: scale(1, 1); 112 | opacity: 1; 113 | } 114 | } 115 | 116 | @keyframes mobile { 117 | from { 118 | opacity: 0.6; 119 | } 120 | to { 121 | opacity: 1; 122 | } 123 | } 124 | 125 | @keyframes fadein { 126 | from { 127 | opacity: 0.2; 128 | } 129 | to { 130 | opacity: 1; 131 | } 132 | } 133 | 134 | #background { 135 | position: fixed; 136 | top: 0; 137 | bottom: 0; 138 | left: 0; 139 | right: 0; 140 | background: #e3e3e3b0; 141 | backdrop-filter: blur(5px); 142 | z-index: var(--background-z-index); 143 | 144 | animation-name: fadein; 145 | animation-duration: 250ms; 146 | } 147 | 148 | #headerContainer { 149 | display: flex; 150 | justify-content: space-between; 151 | margin: 40px; 152 | margin-bottom: 32px; 153 | } 154 | 155 | #headerContainer h1 { 156 | font-size: 34px; 157 | color: #3c3c3c; 158 | margin-top: 20px; 159 | margin-bottom: 7px; 160 | } 161 | 162 | #headerContainer img { 163 | height: 122px; 164 | width: 122px; 165 | background: lightgrey; 166 | border-radius: 10px; 167 | padding: 12px; 168 | border-radius: 24px; 169 | margin-right: 24px; 170 | } 171 | 172 | #buttonsContainer { 173 | display: flex; 174 | justify-content: flex-end; 175 | position: relative; 176 | height: 100px; 177 | 178 | background: #dedede75; 179 | width: 100%; 180 | right: 0em; 181 | border-radius: 0px 0px 12px 12px; 182 | } 183 | 184 | #openButton, 185 | #installButton, 186 | #installCancelButton { 187 | text-align: center; 188 | align-content: center; 189 | align-self: center; 190 | vertical-align: middle; 191 | justify-self: flex-end; 192 | line-height: 200%; 193 | flex: 0 0 auto; 194 | display: inline-block; 195 | background: #0078d4; 196 | color: #ffffff; 197 | cursor: pointer; 198 | border: solid 1px rgba(0, 0, 0, 0); 199 | outline: none; 200 | } 201 | 202 | #openButton { 203 | background: var(--install-button-color); 204 | } 205 | 206 | #openButton:focus { 207 | outline: auto; 208 | outline: -webkit-focus-ring-color auto 1px; 209 | } 210 | 211 | #installButton, 212 | #installCancelButton { 213 | min-width: 130px; 214 | margin-right: 30px; 215 | background: var(--install-button-color); 216 | border-radius: 20px; 217 | font-weight: 600; 218 | font-size: 14px; 219 | line-height: 21px; 220 | padding-top: 10px; 221 | padding-bottom: 9px; 222 | padding-left: 20px; 223 | padding-right: 20px; 224 | outline: none; 225 | color: white; 226 | } 227 | 228 | #closeButton { 229 | background: transparent; 230 | border: none; 231 | color: black; 232 | padding-left: 12px; 233 | padding-right: 12px; 234 | padding-top: 4px; 235 | padding-bottom: 4px; 236 | border-radius: 20px; 237 | font-weight: 600; 238 | outline: none; 239 | cursor: pointer; 240 | align-self: self-end; 241 | } 242 | 243 | #closeButton:focus, 244 | #installButton:focus, 245 | #installCancelButton:focus { 246 | box-shadow: 0 0 0 3px var(--install-focus-color); 247 | } 248 | 249 | #contentContainer { 250 | margin-left: 40px; 251 | margin-right: 40px; 252 | flex: 1; 253 | } 254 | 255 | #contentContainer h3 { 256 | font-size: 22px; 257 | color: #3c3c3c; 258 | margin-bottom: 12px; 259 | } 260 | 261 | #contentContainer p { 262 | font-size: 14px; 263 | color: #3c3c3c; 264 | } 265 | 266 | #featuresScreenDiv { 267 | display: flex; 268 | justify-content: space-around; 269 | align-items: center; 270 | margin-right: 20px; 271 | } 272 | 273 | #featuresScreenDiv h3 { 274 | font-style: normal; 275 | font-weight: 600; 276 | font-size: 22px; 277 | line-height: 225%; 278 | margin-top: 0px; 279 | } 280 | 281 | #keyFeatures { 282 | overflow: hidden; 283 | padding-right: 2em; 284 | } 285 | 286 | #keyFeatures ul { 287 | padding-inline-start: 22px; 288 | margin-block-start: 12px; 289 | } 290 | 291 | #featuresScreenDiv #keyFeatures li { 292 | font-style: normal; 293 | font-weight: 600; 294 | font-size: 16px; 295 | line-height: 29px; 296 | color: rgba(51, 51, 51, 0.72); 297 | } 298 | 299 | #screenshotsContainer { 300 | max-height: 220px; 301 | display: flex; 302 | max-width: 30em; 303 | } 304 | 305 | #screenshotsContainer button { 306 | border: none; 307 | width: 4em; 308 | 309 | transition: background-color 0.2s; 310 | } 311 | 312 | #screenshotsContainer button:focus, 313 | #screenshotsContainer button:hover { 314 | background-color: #bbbbbb; 315 | } 316 | 317 | #screenshotsContainer button svg { 318 | width: 28px; 319 | fill: #6b6969; 320 | } 321 | 322 | #screenshots { 323 | display: flex; 324 | scroll-snap-type: x mandatory; 325 | flex-wrap: wrap; 326 | flex-direction: column; 327 | overflow-x: scroll; 328 | 329 | width: 22em; 330 | max-height: 220px; 331 | 332 | -webkit-overflow-scrolling: touch; 333 | } 334 | 335 | #screenshots div { 336 | display: flex; 337 | align-items: center; 338 | justify-content: center; 339 | scroll-snap-align: start; 340 | 341 | height: 14em; 342 | width: 100%; 343 | 344 | background: #efefef; 345 | } 346 | 347 | #screenshots img { 348 | height: 100%; 349 | object-fit: contain; 350 | } 351 | 352 | #screenshots::-webkit-scrollbar { 353 | display: none; 354 | } 355 | 356 | #tagsDiv { 357 | margin-top: 1em; 358 | margin-bottom: 1em; 359 | } 360 | 361 | #desc { 362 | width: 100%; 363 | max-width: 40em; 364 | font-size: 14px; 365 | color: #7e7e7e; 366 | text-overflow: ellipsis; 367 | overflow: hidden; 368 | } 369 | 370 | #logoContainer { 371 | display: flex; 372 | } 373 | 374 | #tagsDiv span { 375 | background: grey; 376 | color: white; 377 | padding-left: 12px; 378 | padding-right: 12px; 379 | padding-bottom: 4px; 380 | font-weight: bold; 381 | border-radius: 24px; 382 | margin-right: 12px; 383 | padding-top: 1px; 384 | } 385 | 386 | #iosText { 387 | color: var(--install-button-color); 388 | text-align: center; 389 | font-weight: bold; 390 | 391 | position: fixed; 392 | bottom: 0; 393 | left: 0; 394 | right: 0; 395 | backdrop-filter: blur(10px); 396 | background: rgba(239, 239, 239, 0.17); 397 | margin: 0; 398 | padding: 2em; 399 | } 400 | 401 | #manifest-description { 402 | white-space: pre-wrap; 403 | } 404 | 405 | @media (max-height: 780px) { 406 | #buttonsContainer { 407 | height: 70px; 408 | background: transparent; 409 | } 410 | } 411 | 412 | @media (max-width: 1220px) { 413 | #installModal { 414 | margin: 0; 415 | border-radius: 0px; 416 | min-height: 100%; 417 | width: 100%; 418 | 419 | animation-name: mobile; 420 | animation-duration: 250ms; 421 | } 422 | 423 | #screenshots { 424 | justify-content: center; 425 | } 426 | } 427 | 428 | @media (max-width: 962px) { 429 | #headerContainer h1 { 430 | margin-top: 0; 431 | margin-bottom: 0; 432 | } 433 | 434 | #logoContainer { 435 | align-items: center; 436 | } 437 | 438 | #desc { 439 | display: none; 440 | } 441 | 442 | #headerContainer { 443 | margin-bottom: 24px; 444 | } 445 | 446 | #headerContainer img { 447 | height: 42px; 448 | width: 42px; 449 | } 450 | } 451 | 452 | @media (max-width: 800px) { 453 | #background { 454 | display: none; 455 | } 456 | 457 | #installModal { 458 | overflow: scroll; 459 | box-shadow: none; 460 | max-width: 100%; 461 | height: 100%; 462 | } 463 | 464 | #screenshotsContainer { 465 | width: 100%; 466 | } 467 | 468 | #screenshots img { 469 | height: 180px; 470 | } 471 | 472 | #buttonsContainer { 473 | display: flex; 474 | justify-content: center; 475 | bottom: 0; 476 | margin-bottom: 0; 477 | border-radius: 0; 478 | 479 | padding-top: 1em; 480 | padding-bottom: 1em; 481 | } 482 | 483 | #buttonsContainer #installButton { 484 | margin-right: 0px; 485 | } 486 | 487 | #featuresScreenDiv { 488 | flex-direction: column; 489 | align-items: flex-start; 490 | margin-right: 0px; 491 | } 492 | 493 | #headerContainer { 494 | margin: 20px; 495 | } 496 | 497 | #desc { 498 | display: none; 499 | } 500 | 501 | #contentContainer { 502 | margin-left: 20px; 503 | margin-right: 20px; 504 | margin-bottom: 5em; 505 | } 506 | 507 | #headerContainer img { 508 | height: 60px; 509 | width: 60px; 510 | margin-right: 12px; 511 | } 512 | 513 | #buttonsContainer { 514 | position: fixed; 515 | bottom: 0; 516 | background: #efefef2b; 517 | backdrop-filter: blur(10px); 518 | } 519 | } 520 | 521 | @media (max-width: 400px) { 522 | #headerContainer h1 { 523 | font-size: 26px; 524 | } 525 | 526 | #headerContainer img { 527 | height: 40px; 528 | width: 40px; 529 | } 530 | 531 | #featuresScreenDiv h3 { 532 | font-size: 18px; 533 | margin-bottom: 0px; 534 | } 535 | 536 | #keyFeatures ul { 537 | margin-top: 0px; 538 | } 539 | } 540 | 541 | @media all and (display-mode: standalone) { 542 | button { 543 | display: none; 544 | } 545 | } 546 | 547 | @media (prefers-color-scheme: dark) { 548 | :host { 549 | --modal-background-color: black; 550 | } 551 | 552 | #installModal h1, 553 | #installModal h2, 554 | #installModal h3, 555 | #installModal p, 556 | #featuresScreenDiv #keyFeatures li { 557 | color: #ffffff; 558 | } 559 | 560 | #closeButton svg path { 561 | fill: #ffffff; 562 | opacity: 1; 563 | } 564 | 565 | #buttonsContainer { 566 | background: rgb(36 36 36); 567 | } 568 | } 569 | 570 | /* 08-26-2020: supported by only safari desktop */ 571 | @media (inverted-colors: inverted) { 572 | :host { 573 | --install-focus-color: #6e6363; 574 | --install-button-color: #ff872b; 575 | --modal-background-color: black; 576 | } 577 | 578 | #installModal h1, 579 | #installModal h2, 580 | #installModal h3, 581 | #installModal p, 582 | #featuresScreenDiv #keyFeatures li { 583 | color: #ffffff; 584 | } 585 | 586 | #closeButton svg path { 587 | fill: #ffffff; 588 | opacity: 1; 589 | } 590 | 591 | #buttonsContainer { 592 | background: rgb(36 36 36); 593 | } 594 | } 595 | `; 596 | } 597 | async firstUpdated() { 598 | if (this.manifestpath) { 599 | try { 600 | await this.getManifestData(); 601 | } 602 | catch (err) { 603 | console.error("Error getting manifest, check that you have a valid web manifest"); 604 | } 605 | } 606 | if ("getInstalledRelatedApps" in navigator) { 607 | this.relatedApps = await navigator.getInstalledRelatedApps(); 608 | } 609 | } 610 | handleInstallPromptEvent(event) { 611 | this.deferredprompt = event; 612 | this.hasprompt = true; 613 | event.preventDefault(); 614 | } 615 | // Check that the manifest has our 3 required properties 616 | // If not console an error to the user and return 617 | checkManifest(manifestData) { 618 | if (!manifestData.icons || !manifestData.icons[0]) { 619 | console.error("Your web manifest must have atleast one icon listed"); 620 | return; 621 | } 622 | if (!manifestData.name) { 623 | console.error("Your web manifest must have a name listed"); 624 | return; 625 | } 626 | if (!manifestData.description) { 627 | console.error("Your web manifest must have a description listed"); 628 | return; 629 | } 630 | } 631 | async getManifestData() { 632 | try { 633 | const response = await fetch(this.manifestpath); 634 | const data = await response.json(); 635 | this.manifestdata = data; 636 | if (this.manifestdata) { 637 | this.checkManifest(this.manifestdata); 638 | return data; 639 | } 640 | } 641 | catch (err) { } 642 | return null; 643 | } 644 | scrollToLeft() { 645 | var _a; 646 | const screenshotsDiv = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("#screenshots"); 647 | // screenshotsDiv.scrollBy(-10, 0); 648 | screenshotsDiv === null || screenshotsDiv === void 0 ? void 0 : screenshotsDiv.scrollBy({ 649 | // left: -15, 650 | left: -screenshotsDiv.clientWidth, 651 | top: 0, 652 | behavior: "smooth", 653 | }); 654 | } 655 | scrollToRight() { 656 | var _a; 657 | const screenshotsDiv = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("#screenshots"); 658 | // screenshotsDiv.scrollBy(10, 0); 659 | screenshotsDiv === null || screenshotsDiv === void 0 ? void 0 : screenshotsDiv.scrollBy({ 660 | // left: 15, 661 | left: screenshotsDiv.clientWidth, 662 | top: 0, 663 | behavior: "smooth", 664 | }); 665 | } 666 | openPrompt() { 667 | this.openmodal = true; 668 | let event = new CustomEvent("show"); 669 | this.dispatchEvent(event); 670 | this.updateComplete.then(() => { 671 | var _a, _b; 672 | (_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("#closeButton")) === null || _b === void 0 ? void 0 : _b.focus(); 673 | }); 674 | } 675 | closePrompt() { 676 | this.openmodal = false; 677 | let event = new CustomEvent("hide"); 678 | this.dispatchEvent(event); 679 | } 680 | shouldShowInstall() { 681 | const eligibleUser = this.isSupportingBrowser && 682 | this.relatedApps.length < 1 && 683 | (this.hasprompt || this.isIOS); 684 | return eligibleUser; 685 | } 686 | async install() { 687 | if (this.deferredprompt) { 688 | this.deferredprompt.prompt(); 689 | let event = new CustomEvent("show"); 690 | this.dispatchEvent(event); 691 | const choiceResult = await this.deferredprompt.userChoice; 692 | if (choiceResult.outcome === "accepted") { 693 | console.log("Your PWA has been installed"); 694 | await this.cancel(); 695 | this.installed = true; 696 | let event = new CustomEvent("hide"); 697 | this.dispatchEvent(event); 698 | return true; 699 | } 700 | else { 701 | console.log("User chose to not install your PWA"); 702 | await this.cancel(); 703 | // set installed to true because we dont 704 | // want to show the install button to 705 | // a user who chose not to install 706 | this.installed = true; 707 | let event = new CustomEvent("hide"); 708 | this.dispatchEvent(event); 709 | } 710 | } 711 | return false; 712 | } 713 | getInstalledStatus() { 714 | // cast to any because the typescript navigator object 715 | // does not have this non standard safari object 716 | if (navigator.standalone) { 717 | return navigator.standalone; 718 | } 719 | else if (matchMedia("(display-mode: standalone)").matches) { 720 | return true; 721 | } 722 | else { 723 | return false; 724 | } 725 | } 726 | cancel() { 727 | return new Promise((resolve, reject) => { 728 | this.openmodal = false; 729 | if (this.hasAttribute("openmodal")) { 730 | this.removeAttribute("openmodal"); 731 | } 732 | let event = new CustomEvent("hide"); 733 | this.dispatchEvent(event); 734 | resolve(); 735 | }); 736 | } 737 | focusOut() { 738 | console.log("focus out"); 739 | } 740 | render() { 741 | return html ` 742 | ${("standalone" in navigator && 743 | navigator.standalone === false) || 744 | (this.usecustom !== true && 745 | this.shouldShowInstall() && 746 | this.installed === false) 747 | ? html `` 754 | : null} 755 | ${this.openmodal === true 756 | ? html ` 757 | 758 | ${this.openmodal 759 | ? html `
` 763 | : null} 764 |
765 |
766 |
767 | App Logo 773 | 774 |
775 |

776 | ${this.manifestdata.short_name || 777 | this.manifestdata.name} 778 |

779 | 780 |

${this.explainer}

781 |
782 |
783 | 784 | 805 |
806 | 807 |
808 |
809 | ${this.manifestdata.features 810 | ? html `
811 |

${this.featuresheader}

812 |
    813 | ${this.manifestdata.features 814 | ? this.manifestdata.features.map((feature) => { 815 | return html `
  • ${feature}
  • `; 816 | }) 817 | : null} 818 |
819 |
820 |
` 821 | : null} 822 | ${this.manifestdata.screenshots 823 | ? html ` 824 |
825 | 838 |
839 | ${this.manifestdata.screenshots.map((screen) => { 840 | return html ` 841 |
842 | App Screenshot 846 |
847 | `; 848 | })} 849 |
850 | 863 |
864 | ` 865 | : null} 866 |
867 | 868 |
869 |

${this.descriptionheader}

870 |

871 | ${this.manifestdata.description} 872 |

873 |
874 |
875 | 876 | ${!this.isIOS 877 | ? html `
878 | ${this.deferredprompt 879 | ? html `` 885 | : html ``} 891 |
892 |
` 893 | : html `

${this.iosinstallinfotext}

`} 894 | 895 | 896 | ` 897 | : null} 898 | `; 899 | } 900 | }; 901 | __decorate([ 902 | property({ type: String }) 903 | ], pwainstall.prototype, "manifestpath", void 0); 904 | __decorate([ 905 | property({ type: String }) 906 | ], pwainstall.prototype, "iconpath", void 0); 907 | __decorate([ 908 | property({ type: Object }) 909 | ], pwainstall.prototype, "manifestdata", void 0); 910 | __decorate([ 911 | property({ type: Boolean }) 912 | ], pwainstall.prototype, "openmodal", void 0); 913 | __decorate([ 914 | property({ type: Boolean }) 915 | ], pwainstall.prototype, "isSupportingBrowser", void 0); 916 | __decorate([ 917 | property({ type: Boolean }) 918 | ], pwainstall.prototype, "isIOS", void 0); 919 | __decorate([ 920 | property({ type: Boolean }) 921 | ], pwainstall.prototype, "installed", void 0); 922 | __decorate([ 923 | property({ type: Boolean }) 924 | ], pwainstall.prototype, "hasprompt", void 0); 925 | __decorate([ 926 | property({ type: Boolean }) 927 | ], pwainstall.prototype, "usecustom", void 0); 928 | __decorate([ 929 | property({ type: Array }) 930 | ], pwainstall.prototype, "relatedApps", void 0); 931 | __decorate([ 932 | property({ type: String }) 933 | ], pwainstall.prototype, "explainer", void 0); 934 | __decorate([ 935 | property({ type: String }) 936 | ], pwainstall.prototype, "featuresheader", void 0); 937 | __decorate([ 938 | property({ type: String }) 939 | ], pwainstall.prototype, "descriptionheader", void 0); 940 | __decorate([ 941 | property({ type: String }) 942 | ], pwainstall.prototype, "installbuttontext", void 0); 943 | __decorate([ 944 | property({ type: String }) 945 | ], pwainstall.prototype, "cancelbuttontext", void 0); 946 | __decorate([ 947 | property({ type: String }) 948 | ], pwainstall.prototype, "iosinstallinfotext", void 0); 949 | __decorate([ 950 | property() 951 | ], pwainstall.prototype, "deferredprompt", void 0); 952 | pwainstall = __decorate([ 953 | customElement("pwa-install") 954 | ], pwainstall); 955 | export { pwainstall }; 956 | -------------------------------------------------------------------------------- /build/pwa-install.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"pwa-install.js","sourceRoot":"","sources":["../src/pwa-install.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /coverage/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* yellow */ 156 | .cbranch-no { background: yellow !important; color: #111; } 157 | /* dark red */ 158 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 159 | .low .chart { border:1px solid #C21F39 } 160 | .highlighted, 161 | .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ 162 | background: #C21F39 !important; 163 | } 164 | /* medium red */ 165 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 166 | /* light red */ 167 | .low, .cline-no { background:#FCE1E5 } 168 | /* light green */ 169 | .high, .cline-yes { background:rgb(230,245,208) } 170 | /* medium green */ 171 | .cstat-yes { background:rgb(161,215,106) } 172 | /* dark green */ 173 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 174 | .high .chart { border:1px solid rgb(77,146,33) } 175 | /* dark yellow (gold) */ 176 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; } 177 | .medium .chart { border:1px solid #f9cd0b; } 178 | /* light yellow */ 179 | .medium { background: #fff4c2; } 180 | 181 | .cstat-skip { background: #ddd; color: #111; } 182 | .fstat-skip { background: #ddd; color: #111 !important; } 183 | .cbranch-skip { background: #ddd !important; color: #111; } 184 | 185 | span.cline-neutral { background: #eaeaea; } 186 | 187 | .coverage-summary td.empty { 188 | opacity: .5; 189 | padding-top: 4px; 190 | padding-bottom: 4px; 191 | line-height: 1; 192 | color: #888; 193 | } 194 | 195 | .cover-fill, .cover-empty { 196 | display:inline-block; 197 | height: 12px; 198 | } 199 | .chart { 200 | line-height: 0; 201 | } 202 | .cover-empty { 203 | background: white; 204 | } 205 | .cover-full { 206 | border-right: none !important; 207 | } 208 | pre.prettyprint { 209 | border: none !important; 210 | padding: 0 !important; 211 | margin: 0 !important; 212 | } 213 | .com { color: #999 !important; } 214 | .ignore-none { color: #999; font-weight: normal; } 215 | 216 | .wrapper { 217 | min-height: 100%; 218 | height: auto !important; 219 | height: 100%; 220 | margin: 0 auto -48px; 221 | } 222 | .footer, .push { 223 | height: 48px; 224 | } 225 | -------------------------------------------------------------------------------- /coverage/block-navigation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var jumpToCode = (function init() { 3 | // Classes of code we would like to highlight in the file view 4 | var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; 5 | 6 | // Elements to highlight in the file listing view 7 | var fileListingElements = ['td.pct.low']; 8 | 9 | // We don't want to select elements that are direct descendants of another match 10 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` 11 | 12 | // Selecter that finds elements on the page to which we can jump 13 | var selector = 14 | fileListingElements.join(', ') + 15 | ', ' + 16 | notSelector + 17 | missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` 18 | 19 | // The NodeList of matching elements 20 | var missingCoverageElements = document.querySelectorAll(selector); 21 | 22 | var currentIndex; 23 | 24 | function toggleClass(index) { 25 | missingCoverageElements 26 | .item(currentIndex) 27 | .classList.remove('highlighted'); 28 | missingCoverageElements.item(index).classList.add('highlighted'); 29 | } 30 | 31 | function makeCurrent(index) { 32 | toggleClass(index); 33 | currentIndex = index; 34 | missingCoverageElements.item(index).scrollIntoView({ 35 | behavior: 'smooth', 36 | block: 'center', 37 | inline: 'center' 38 | }); 39 | } 40 | 41 | function goToPrevious() { 42 | var nextIndex = 0; 43 | if (typeof currentIndex !== 'number' || currentIndex === 0) { 44 | nextIndex = missingCoverageElements.length - 1; 45 | } else if (missingCoverageElements.length > 1) { 46 | nextIndex = currentIndex - 1; 47 | } 48 | 49 | makeCurrent(nextIndex); 50 | } 51 | 52 | function goToNext() { 53 | var nextIndex = 0; 54 | 55 | if ( 56 | typeof currentIndex === 'number' && 57 | currentIndex < missingCoverageElements.length - 1 58 | ) { 59 | nextIndex = currentIndex + 1; 60 | } 61 | 62 | makeCurrent(nextIndex); 63 | } 64 | 65 | return function jump(event) { 66 | switch (event.which) { 67 | case 78: // n 68 | case 74: // j 69 | goToNext(); 70 | break; 71 | case 66: // b 72 | case 75: // k 73 | case 80: // p 74 | goToPrevious(); 75 | break; 76 | } 77 | }; 78 | })(); 79 | window.addEventListener('keydown', jumpToCode); 80 | -------------------------------------------------------------------------------- /coverage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for All files 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files 20 |

21 |
22 |
23 | 43.08% 24 | Statements 25 | 28/65 26 |
27 |
28 | 43.48% 29 | Branches 30 | 30/69 31 |
32 |
33 | 35.71% 34 | Functions 35 | 10/28 36 |
37 |
38 | 42.62% 39 | Lines 40 | 26/61 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
build
43.08%28/6543.48%30/6935.71%10/2842.62%26/61
dist
0%0/00%0/00%0/00%0/0
92 |
93 |
94 | 98 | 99 | 100 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /coverage/lcov.info: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwa-builder/pwa-install/9b2895a539b3bedb520b23378c3a556e78f24e82/coverage/lcov.info -------------------------------------------------------------------------------- /coverage/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /coverage/prettify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /coverage/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwa-builder/pwa-install/9b2895a539b3bedb520b23378c3a556e78f24e82/coverage/sort-arrow-sprite.png -------------------------------------------------------------------------------- /coverage/sorter.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var addSorting = (function() { 3 | 'use strict'; 4 | var cols, 5 | currentSort = { 6 | index: 0, 7 | desc: false 8 | }; 9 | 10 | // returns the summary table element 11 | function getTable() { 12 | return document.querySelector('.coverage-summary'); 13 | } 14 | // returns the thead element of the summary table 15 | function getTableHeader() { 16 | return getTable().querySelector('thead tr'); 17 | } 18 | // returns the tbody element of the summary table 19 | function getTableBody() { 20 | return getTable().querySelector('tbody'); 21 | } 22 | // returns the th element for nth column 23 | function getNthColumn(n) { 24 | return getTableHeader().querySelectorAll('th')[n]; 25 | } 26 | 27 | // loads all columns 28 | function loadColumns() { 29 | var colNodes = getTableHeader().querySelectorAll('th'), 30 | colNode, 31 | cols = [], 32 | col, 33 | i; 34 | 35 | for (i = 0; i < colNodes.length; i += 1) { 36 | colNode = colNodes[i]; 37 | col = { 38 | key: colNode.getAttribute('data-col'), 39 | sortable: !colNode.getAttribute('data-nosort'), 40 | type: colNode.getAttribute('data-type') || 'string' 41 | }; 42 | cols.push(col); 43 | if (col.sortable) { 44 | col.defaultDescSort = col.type === 'number'; 45 | colNode.innerHTML = 46 | colNode.innerHTML + ''; 47 | } 48 | } 49 | return cols; 50 | } 51 | // attaches a data attribute to every tr element with an object 52 | // of data values keyed by column name 53 | function loadRowData(tableRow) { 54 | var tableCols = tableRow.querySelectorAll('td'), 55 | colNode, 56 | col, 57 | data = {}, 58 | i, 59 | val; 60 | for (i = 0; i < tableCols.length; i += 1) { 61 | colNode = tableCols[i]; 62 | col = cols[i]; 63 | val = colNode.getAttribute('data-value'); 64 | if (col.type === 'number') { 65 | val = Number(val); 66 | } 67 | data[col.key] = val; 68 | } 69 | return data; 70 | } 71 | // loads all row data 72 | function loadData() { 73 | var rows = getTableBody().querySelectorAll('tr'), 74 | i; 75 | 76 | for (i = 0; i < rows.length; i += 1) { 77 | rows[i].data = loadRowData(rows[i]); 78 | } 79 | } 80 | // sorts the table using the data for the ith column 81 | function sortByIndex(index, desc) { 82 | var key = cols[index].key, 83 | sorter = function(a, b) { 84 | a = a.data[key]; 85 | b = b.data[key]; 86 | return a < b ? -1 : a > b ? 1 : 0; 87 | }, 88 | finalSorter = sorter, 89 | tableBody = document.querySelector('.coverage-summary tbody'), 90 | rowNodes = tableBody.querySelectorAll('tr'), 91 | rows = [], 92 | i; 93 | 94 | if (desc) { 95 | finalSorter = function(a, b) { 96 | return -1 * sorter(a, b); 97 | }; 98 | } 99 | 100 | for (i = 0; i < rowNodes.length; i += 1) { 101 | rows.push(rowNodes[i]); 102 | tableBody.removeChild(rowNodes[i]); 103 | } 104 | 105 | rows.sort(finalSorter); 106 | 107 | for (i = 0; i < rows.length; i += 1) { 108 | tableBody.appendChild(rows[i]); 109 | } 110 | } 111 | // removes sort indicators for current column being sorted 112 | function removeSortIndicators() { 113 | var col = getNthColumn(currentSort.index), 114 | cls = col.className; 115 | 116 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 117 | col.className = cls; 118 | } 119 | // adds sort indicators for current column being sorted 120 | function addSortIndicators() { 121 | getNthColumn(currentSort.index).className += currentSort.desc 122 | ? ' sorted-desc' 123 | : ' sorted'; 124 | } 125 | // adds event listeners for all sorter widgets 126 | function enableUI() { 127 | var i, 128 | el, 129 | ithSorter = function ithSorter(i) { 130 | var col = cols[i]; 131 | 132 | return function() { 133 | var desc = col.defaultDescSort; 134 | 135 | if (currentSort.index === i) { 136 | desc = !currentSort.desc; 137 | } 138 | sortByIndex(i, desc); 139 | removeSortIndicators(); 140 | currentSort.index = i; 141 | currentSort.desc = desc; 142 | addSortIndicators(); 143 | }; 144 | }; 145 | for (i = 0; i < cols.length; i += 1) { 146 | if (cols[i].sortable) { 147 | // add the click event handler on the th so users 148 | // dont have to click on those tiny arrows 149 | el = getNthColumn(i).querySelector('.sorter').parentElement; 150 | if (el.addEventListener) { 151 | el.addEventListener('click', ithSorter(i)); 152 | } else { 153 | el.attachEvent('onclick', ithSorter(i)); 154 | } 155 | } 156 | } 157 | } 158 | // adds sorting functionality to the UI 159 | return function() { 160 | if (!getTable()) { 161 | return; 162 | } 163 | cols = loadColumns(); 164 | loadData(); 165 | addSortIndicators(); 166 | enableUI(); 167 | }; 168 | })(); 169 | 170 | window.addEventListener('load', addSorting); 171 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare global { 4 | interface HTMLElementTagNameMap { 5 | "pwa-install": PWAInstallComponent; 6 | } 7 | 8 | interface PWAInstallComponent extends HTMLElement { 9 | openmodal?: boolean; 10 | usecustom?: boolean; 11 | manifestpath?: string; 12 | explainer?: string; 13 | featuresheader?: string; 14 | descriptionheader?: string; 15 | installbuttontext?: string; 16 | cancelbuttontext?: string; 17 | iosinstallinfotext?: string; 18 | 19 | openPrompt(): void; 20 | closePrompt(): void; 21 | getInstalledStatus(): boolean; 22 | } 23 | } 24 | 25 | export {}; 26 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | const { createDefaultConfig } = require('@open-wc/testing-karma'); 2 | const merge = require('deepmerge'); 3 | 4 | module.exports = config => { 5 | config.set( 6 | merge(createDefaultConfig(config), { 7 | files: [ 8 | // runs all files ending with .test in the test folder, 9 | // can be overwritten by passing a --grep flag. examples: 10 | // 11 | // npm run test -- --grep test/foo/bar.test.js 12 | // npm run test -- --grep test/bar/* 13 | { pattern: config.grep ? config.grep : 'test/**/*.test.js', type: 'module' }, 14 | ], 15 | 16 | plugins: [ 17 | // load plugin 18 | require.resolve('@open-wc/karma-esm'), 19 | 20 | // fallback: resolve any karma- plugins 21 | 'karma-*', 22 | ], 23 | 24 | frameworks: ['esm'], 25 | 26 | // see the karma-esm docs for all options 27 | esm: { 28 | // if you are using 'bare module imports' you will need this option 29 | nodeResolve: true, 30 | }, 31 | 32 | coverageIstanbulReporter: { 33 | thresholds: { 34 | emitWarning: true 35 | } 36 | } 37 | }), 38 | ); 39 | return config; 40 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pwabuilder/pwainstall", 3 | "version": "1.6.9", 4 | "description": "", 5 | "main": "dist/pwa-install.js", 6 | "module": "dist/pwa-install.js", 7 | "types": "index.d.ts", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/pwa-builder/pwa-install.git" 11 | }, 12 | "scripts": { 13 | "test": "./node_modules/.bin/karma start --coverage", 14 | "test:watch": "karma start --auto-watch=true --single-run=false", 15 | "test:update-snapshots": "karma start --update-snapshots", 16 | "test:prune-snapshots": "karma start --prune-snapshots", 17 | "test:compatibility": "karma start --compatibility all --auto-watch=true --single-run=false", 18 | "start": "concurrently \"tsc --watch\" \"npx es-dev-server --node-resolve --compatibility none --open www/\"", 19 | "dev": "concurrently \"tsc --watch\" \"npx es-dev-server --node-resolve --compatibility none --open www/\"", 20 | "build": "tsc && rollup -c" 21 | }, 22 | "author": "PWABuilder Team", 23 | "license": "ISC", 24 | "dependencies": { 25 | "lit-element": "^2.4.0" 26 | }, 27 | "devDependencies": { 28 | "@open-wc/karma-esm": "^3.0.4", 29 | "@open-wc/testing": "^2.5.24", 30 | "@open-wc/testing-helpers": "^1.8.9", 31 | "@open-wc/testing-karma": "^4.0.4", 32 | "@rollup/plugin-strip": "^2.0.0", 33 | "concurrently": "^5.3.0", 34 | "deepmerge": "^4.2.2", 35 | "es-dev-server": "^1.57.3", 36 | "karma": "^5.1.1", 37 | "karma-threshold-reporter": "^0.1.15", 38 | "rollup": "^2.26.5", 39 | "rollup-plugin-minify-html-literals": "^1.2.5", 40 | "rollup-plugin-node-resolve": "^5.2.0", 41 | "rollup-plugin-terser": "^7.0.0", 42 | "typescript": "^4.0.2" 43 | }, 44 | "repository": { 45 | "type": "git", 46 | "url": "git+https://github.com/pwa-builder/pwa-install.git" 47 | }, 48 | "bugs": { 49 | "url": "https://github.com/pwa-builder/pwa-install/issues" 50 | }, 51 | "homepage": "https://github.com/pwa-builder/pwa-install#readme" 52 | } 53 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | 2 | import resolve from 'rollup-plugin-node-resolve'; 3 | import { terser } from 'rollup-plugin-terser'; 4 | import minifyHTML from 'rollup-plugin-minify-html-literals'; 5 | import strip from '@rollup/plugin-strip'; 6 | 7 | export default { 8 | input: ['build/pwa-install.js'], 9 | output: { 10 | file: 'dist/pwa-install.js', 11 | format: 'es', 12 | sourcemap: false 13 | }, 14 | plugins: [ 15 | resolve(), 16 | minifyHTML(), 17 | terser(), 18 | strip({ 19 | functions: ['console.log'] 20 | }) 21 | ] 22 | }; -------------------------------------------------------------------------------- /src/pwa-install.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, html, customElement, property, css } from "lit-element"; 2 | 3 | interface ManifestData { 4 | name: string; 5 | short_name: string; 6 | description: string; 7 | screenshots: Array; 8 | features: Array; 9 | icons: Array; 10 | } 11 | 12 | @customElement("pwa-install") 13 | export class pwainstall extends LitElement implements PWAInstallComponent { 14 | @property({ type: String }) manifestpath: string = "manifest.json"; 15 | @property({ type: String }) iconpath: string = ""; 16 | @property({ type: Object }) manifestdata: ManifestData = { 17 | name: "", 18 | short_name: "", 19 | description: "", 20 | icons: [], 21 | screenshots: [], 22 | features: [] 23 | }; 24 | 25 | @property({ type: Boolean }) openmodal: boolean = false; 26 | @property({ type: Boolean }) isSupportingBrowser: boolean; 27 | @property({ type: Boolean }) isIOS: boolean; 28 | @property({ type: Boolean }) installed: boolean; 29 | @property({ type: Boolean }) hasprompt: boolean = false; 30 | @property({ type: Boolean }) usecustom: boolean = false; 31 | @property({ type: Array }) relatedApps: any[] = []; 32 | 33 | @property({ type: String }) explainer: string = 34 | "This app can be installed on your PC or mobile device. This will allow this web app to look and behave like any other installed app. You will find it in your app lists and be able to pin it to your home screen, start menus or task bars. This installed web app will also be able to safely interact with other apps and your operating system. "; 35 | @property({ type: String }) featuresheader: string = "Key Features"; 36 | @property({ type: String }) descriptionheader: string = "Description"; 37 | @property({ type: String }) installbuttontext: string = "Install"; 38 | @property({ type: String }) cancelbuttontext: string = "Cancel"; 39 | @property({ type: String }) iosinstallinfotext: string = 40 | "Tap the share button and then 'Add to Homescreen'"; 41 | 42 | @property() deferredprompt: any; 43 | 44 | static get styles() { 45 | return css` 46 | :host { 47 | --install-focus-color: #919c9c; 48 | --install-button-color: #0078d4; 49 | --modal-z-index: 9999; 50 | --background-z-index: 9998; 51 | --modal-background-color: white; 52 | } 53 | 54 | button { 55 | outline: none; 56 | } 57 | 58 | #installModalWrapper { 59 | height: 100vh; 60 | width: 100vw; 61 | overflow: auto; 62 | position: fixed; 63 | bottom: 0; 64 | top: 0; 65 | left: 0; 66 | right: 0; 67 | z-index: var(--modal-z-index); 68 | 69 | display: flex; 70 | justify-content: center; 71 | align-items: center; 72 | } 73 | 74 | #descriptionWrapper { 75 | margin-bottom: 3em; 76 | } 77 | 78 | #installModal { 79 | position: absolute; 80 | background: var(--modal-background-color); 81 | font-family: sans-serif; 82 | box-shadow: 0px 25px 26px rgba(32, 36, 50, 0.25), 83 | 0px 5px 9px rgba(51, 58, 83, 0.53); 84 | border-radius: 10px; 85 | display: flex; 86 | flex-direction: column; 87 | padding: 0; 88 | 89 | animation-name: opened; 90 | animation-duration: 150ms; 91 | 92 | z-index: var(--modal-z-index); 93 | 94 | max-width: 56em; 95 | } 96 | 97 | @keyframes opened { 98 | from { 99 | transform: scale(0.8, 0.8); 100 | opacity: 0.4; 101 | } 102 | to { 103 | transform: scale(1, 1); 104 | opacity: 1; 105 | } 106 | } 107 | 108 | @keyframes mobile { 109 | from { 110 | opacity: 0.6; 111 | } 112 | to { 113 | opacity: 1; 114 | } 115 | } 116 | 117 | @keyframes fadein { 118 | from { 119 | opacity: 0.2; 120 | } 121 | to { 122 | opacity: 1; 123 | } 124 | } 125 | 126 | #background { 127 | position: fixed; 128 | top: 0; 129 | bottom: 0; 130 | left: 0; 131 | right: 0; 132 | background: #e3e3e3b0; 133 | backdrop-filter: blur(5px); 134 | z-index: var(--background-z-index); 135 | 136 | animation-name: fadein; 137 | animation-duration: 250ms; 138 | } 139 | 140 | #headerContainer { 141 | display: flex; 142 | justify-content: space-between; 143 | margin: 40px; 144 | margin-bottom: 32px; 145 | } 146 | 147 | #headerContainer h1 { 148 | font-size: 34px; 149 | color: #3c3c3c; 150 | margin-top: 20px; 151 | margin-bottom: 7px; 152 | } 153 | 154 | #headerContainer img { 155 | height: 122px; 156 | width: 122px; 157 | background: lightgrey; 158 | border-radius: 10px; 159 | padding: 12px; 160 | border-radius: 24px; 161 | margin-right: 24px; 162 | } 163 | 164 | #buttonsContainer { 165 | display: flex; 166 | justify-content: flex-end; 167 | position: relative; 168 | height: 100px; 169 | 170 | background: #dedede75; 171 | width: 100%; 172 | right: 0em; 173 | border-radius: 0px 0px 12px 12px; 174 | } 175 | 176 | #openButton, 177 | #installButton, 178 | #installCancelButton { 179 | text-align: center; 180 | align-content: center; 181 | align-self: center; 182 | vertical-align: middle; 183 | justify-self: flex-end; 184 | line-height: 200%; 185 | flex: 0 0 auto; 186 | display: inline-block; 187 | background: #0078d4; 188 | color: #ffffff; 189 | cursor: pointer; 190 | border: solid 1px rgba(0, 0, 0, 0); 191 | outline: none; 192 | } 193 | 194 | #openButton { 195 | background: var(--install-button-color); 196 | } 197 | 198 | #openButton:focus { 199 | outline: auto; 200 | outline: -webkit-focus-ring-color auto 1px; 201 | } 202 | 203 | #installButton, 204 | #installCancelButton { 205 | min-width: 130px; 206 | margin-right: 30px; 207 | background: var(--install-button-color); 208 | border-radius: 20px; 209 | font-weight: 600; 210 | font-size: 14px; 211 | line-height: 21px; 212 | padding-top: 10px; 213 | padding-bottom: 9px; 214 | padding-left: 20px; 215 | padding-right: 20px; 216 | outline: none; 217 | color: white; 218 | } 219 | 220 | #closeButton { 221 | background: transparent; 222 | border: none; 223 | color: black; 224 | padding-left: 12px; 225 | padding-right: 12px; 226 | padding-top: 4px; 227 | padding-bottom: 4px; 228 | border-radius: 20px; 229 | font-weight: 600; 230 | outline: none; 231 | cursor: pointer; 232 | align-self: self-end; 233 | } 234 | 235 | #closeButton:focus, 236 | #installButton:focus, 237 | #installCancelButton:focus { 238 | box-shadow: 0 0 0 3px var(--install-focus-color); 239 | } 240 | 241 | #contentContainer { 242 | margin-left: 40px; 243 | margin-right: 40px; 244 | flex: 1; 245 | } 246 | 247 | #contentContainer h3 { 248 | font-size: 22px; 249 | color: #3c3c3c; 250 | margin-bottom: 12px; 251 | } 252 | 253 | #contentContainer p { 254 | font-size: 14px; 255 | color: #3c3c3c; 256 | } 257 | 258 | #featuresScreenDiv { 259 | display: flex; 260 | justify-content: space-around; 261 | align-items: center; 262 | margin-right: 20px; 263 | } 264 | 265 | #featuresScreenDiv h3 { 266 | font-style: normal; 267 | font-weight: 600; 268 | font-size: 22px; 269 | line-height: 225%; 270 | margin-top: 0px; 271 | } 272 | 273 | #keyFeatures { 274 | overflow: hidden; 275 | padding-right: 2em; 276 | } 277 | 278 | #keyFeatures ul { 279 | padding-inline-start: 22px; 280 | margin-block-start: 12px; 281 | } 282 | 283 | #featuresScreenDiv #keyFeatures li { 284 | font-style: normal; 285 | font-weight: 600; 286 | font-size: 16px; 287 | line-height: 29px; 288 | color: rgba(51, 51, 51, 0.72); 289 | } 290 | 291 | #screenshotsContainer { 292 | max-height: 220px; 293 | display: flex; 294 | max-width: 30em; 295 | } 296 | 297 | #screenshotsContainer button { 298 | border: none; 299 | width: 4em; 300 | 301 | transition: background-color 0.2s; 302 | } 303 | 304 | #screenshotsContainer button:focus, 305 | #screenshotsContainer button:hover { 306 | background-color: #bbbbbb; 307 | } 308 | 309 | #screenshotsContainer button svg { 310 | width: 28px; 311 | fill: #6b6969; 312 | } 313 | 314 | #screenshots { 315 | display: flex; 316 | scroll-snap-type: x mandatory; 317 | flex-wrap: wrap; 318 | flex-direction: column; 319 | overflow-x: scroll; 320 | 321 | width: 22em; 322 | max-height: 220px; 323 | 324 | -webkit-overflow-scrolling: touch; 325 | } 326 | 327 | #screenshots div { 328 | display: flex; 329 | align-items: center; 330 | justify-content: center; 331 | scroll-snap-align: start; 332 | 333 | height: 14em; 334 | width: 100%; 335 | 336 | background: #efefef; 337 | } 338 | 339 | #screenshots img { 340 | height: 100%; 341 | object-fit: contain; 342 | } 343 | 344 | #screenshots::-webkit-scrollbar { 345 | display: none; 346 | } 347 | 348 | #tagsDiv { 349 | margin-top: 1em; 350 | margin-bottom: 1em; 351 | } 352 | 353 | #desc { 354 | width: 100%; 355 | max-width: 40em; 356 | font-size: 14px; 357 | color: #7e7e7e; 358 | text-overflow: ellipsis; 359 | overflow: hidden; 360 | } 361 | 362 | #logoContainer { 363 | display: flex; 364 | } 365 | 366 | #tagsDiv span { 367 | background: grey; 368 | color: white; 369 | padding-left: 12px; 370 | padding-right: 12px; 371 | padding-bottom: 4px; 372 | font-weight: bold; 373 | border-radius: 24px; 374 | margin-right: 12px; 375 | padding-top: 1px; 376 | } 377 | 378 | #iosText { 379 | color: var(--install-button-color); 380 | text-align: center; 381 | font-weight: bold; 382 | 383 | position: fixed; 384 | bottom: 0; 385 | left: 0; 386 | right: 0; 387 | backdrop-filter: blur(10px); 388 | background: rgba(239, 239, 239, 0.17); 389 | margin: 0; 390 | padding: 2em; 391 | } 392 | 393 | #manifest-description { 394 | white-space: pre-wrap; 395 | } 396 | 397 | @media (max-height: 780px) { 398 | #buttonsContainer { 399 | height: 70px; 400 | background: transparent; 401 | } 402 | } 403 | 404 | @media (max-width: 1220px) { 405 | #installModal { 406 | margin: 0; 407 | border-radius: 0px; 408 | min-height: 100%; 409 | width: 100%; 410 | 411 | animation-name: mobile; 412 | animation-duration: 250ms; 413 | } 414 | 415 | #screenshots { 416 | justify-content: center; 417 | } 418 | } 419 | 420 | @media (max-width: 962px) { 421 | #headerContainer h1 { 422 | margin-top: 0; 423 | margin-bottom: 0; 424 | } 425 | 426 | #logoContainer { 427 | align-items: center; 428 | } 429 | 430 | #desc { 431 | display: none; 432 | } 433 | 434 | #headerContainer { 435 | margin-bottom: 24px; 436 | } 437 | 438 | #headerContainer img { 439 | height: 42px; 440 | width: 42px; 441 | } 442 | } 443 | 444 | @media (max-width: 800px) { 445 | #background { 446 | display: none; 447 | } 448 | 449 | #installModal { 450 | overflow: scroll; 451 | box-shadow: none; 452 | max-width: 100%; 453 | height: 100%; 454 | } 455 | 456 | #screenshotsContainer { 457 | width: 100%; 458 | } 459 | 460 | #screenshots img { 461 | height: 180px; 462 | } 463 | 464 | #buttonsContainer { 465 | display: flex; 466 | justify-content: center; 467 | bottom: 0; 468 | margin-bottom: 0; 469 | border-radius: 0; 470 | 471 | padding-top: 1em; 472 | padding-bottom: 1em; 473 | } 474 | 475 | #buttonsContainer #installButton { 476 | margin-right: 0px; 477 | } 478 | 479 | #featuresScreenDiv { 480 | flex-direction: column; 481 | align-items: flex-start; 482 | margin-right: 0px; 483 | } 484 | 485 | #headerContainer { 486 | margin: 20px; 487 | } 488 | 489 | #desc { 490 | display: none; 491 | } 492 | 493 | #contentContainer { 494 | margin-left: 20px; 495 | margin-right: 20px; 496 | margin-bottom: 5em; 497 | } 498 | 499 | #headerContainer img { 500 | height: 60px; 501 | width: 60px; 502 | margin-right: 12px; 503 | } 504 | 505 | #buttonsContainer { 506 | position: fixed; 507 | bottom: 0; 508 | background: #efefef2b; 509 | backdrop-filter: blur(10px); 510 | } 511 | } 512 | 513 | @media (max-width: 400px) { 514 | #headerContainer h1 { 515 | font-size: 26px; 516 | } 517 | 518 | #headerContainer img { 519 | height: 40px; 520 | width: 40px; 521 | } 522 | 523 | #featuresScreenDiv h3 { 524 | font-size: 18px; 525 | margin-bottom: 0px; 526 | } 527 | 528 | #keyFeatures ul { 529 | margin-top: 0px; 530 | } 531 | } 532 | 533 | @media all and (display-mode: standalone) { 534 | button { 535 | display: none; 536 | } 537 | } 538 | 539 | @media (prefers-color-scheme: dark) { 540 | :host { 541 | --modal-background-color: black; 542 | } 543 | 544 | #installModal h1, 545 | #installModal h2, 546 | #installModal h3, 547 | #installModal p, 548 | #featuresScreenDiv #keyFeatures li { 549 | color: #ffffff; 550 | } 551 | 552 | #closeButton svg path { 553 | fill: #ffffff; 554 | opacity: 1; 555 | } 556 | 557 | #buttonsContainer { 558 | background: rgb(36 36 36); 559 | } 560 | } 561 | 562 | /* 08-26-2020: supported by only safari desktop */ 563 | @media (inverted-colors: inverted) { 564 | :host { 565 | --install-focus-color: #6e6363; 566 | --install-button-color: #ff872b; 567 | --modal-background-color: black; 568 | } 569 | 570 | #installModal h1, 571 | #installModal h2, 572 | #installModal h3, 573 | #installModal p, 574 | #featuresScreenDiv #keyFeatures li { 575 | color: #ffffff; 576 | } 577 | 578 | #closeButton svg path { 579 | fill: #ffffff; 580 | opacity: 1; 581 | } 582 | 583 | #buttonsContainer { 584 | background: rgb(36 36 36); 585 | } 586 | } 587 | `; 588 | } 589 | 590 | constructor() { 591 | super(); 592 | 593 | // check for beforeinstallprompt support 594 | this.isSupportingBrowser = window.hasOwnProperty( 595 | "BeforeInstallPromptEvent" 596 | ); 597 | 598 | // handle iOS specifically 599 | // this includes the regular iPad 600 | // and the iPad pro 601 | // but not macOS 602 | this.isIOS = 603 | navigator.userAgent.includes("iPhone") || 604 | navigator.userAgent.includes("iPad") || 605 | (navigator.userAgent.includes("Macintosh") && 606 | typeof navigator.maxTouchPoints === "number" && 607 | navigator.maxTouchPoints > 2); 608 | 609 | this.installed = false; 610 | 611 | // grab an install event 612 | window.addEventListener("beforeinstallprompt", (event) => 613 | this.handleInstallPromptEvent(event) 614 | ); 615 | 616 | document.addEventListener("keyup", (event) => { 617 | if (event.key === "Escape") { 618 | this.cancel(); 619 | } 620 | }); 621 | } 622 | 623 | async firstUpdated(): Promise { 624 | if (this.manifestpath) { 625 | try { 626 | await this.getManifestData(); 627 | } catch (err) { 628 | console.error( 629 | "Error getting manifest, check that you have a valid web manifest" 630 | ); 631 | } 632 | } 633 | 634 | if ("getInstalledRelatedApps" in navigator) { 635 | this.relatedApps = await (navigator as any).getInstalledRelatedApps(); 636 | } 637 | } 638 | 639 | handleInstallPromptEvent(event: Event): void { 640 | this.deferredprompt = event; 641 | 642 | this.hasprompt = true; 643 | 644 | event.preventDefault(); 645 | } 646 | 647 | // Check that the manifest has our 3 required properties 648 | // If not console an error to the user and return 649 | checkManifest(manifestData: ManifestData): void { 650 | if (!manifestData.icons || !manifestData.icons[0]) { 651 | console.error("Your web manifest must have atleast one icon listed"); 652 | return; 653 | } 654 | 655 | if (!manifestData.name) { 656 | console.error("Your web manifest must have a name listed"); 657 | return; 658 | } 659 | 660 | if (!manifestData.description) { 661 | console.error("Your web manifest must have a description listed"); 662 | return; 663 | } 664 | } 665 | 666 | async getManifestData(): Promise { 667 | try { 668 | const response = await fetch(this.manifestpath); 669 | const data = await response.json(); 670 | 671 | this.manifestdata = data; 672 | 673 | if (this.manifestdata) { 674 | this.checkManifest(this.manifestdata); 675 | 676 | return data; 677 | } 678 | } catch (err) { 679 | } 680 | 681 | return null; 682 | } 683 | 684 | scrollToLeft(): void { 685 | const screenshotsDiv = this.shadowRoot?.querySelector("#screenshots"); 686 | // screenshotsDiv.scrollBy(-10, 0); 687 | screenshotsDiv?.scrollBy({ 688 | // left: -15, 689 | left: -screenshotsDiv.clientWidth, 690 | top: 0, 691 | behavior: "smooth", 692 | }); 693 | } 694 | 695 | scrollToRight(): void { 696 | const screenshotsDiv = this.shadowRoot?.querySelector("#screenshots"); 697 | // screenshotsDiv.scrollBy(10, 0); 698 | screenshotsDiv?.scrollBy({ 699 | // left: 15, 700 | left: screenshotsDiv.clientWidth, 701 | top: 0, 702 | behavior: "smooth", 703 | }); 704 | } 705 | 706 | public openPrompt(): void { 707 | this.openmodal = true; 708 | 709 | let event = new CustomEvent("show"); 710 | this.dispatchEvent(event); 711 | this.updateComplete.then(() => { 712 | (this.shadowRoot?.querySelector("#closeButton") as HTMLElement)?.focus() 713 | }); 714 | } 715 | 716 | public closePrompt(): void { 717 | this.openmodal = false; 718 | 719 | let event = new CustomEvent("hide"); 720 | this.dispatchEvent(event); 721 | } 722 | 723 | shouldShowInstall() { 724 | const eligibleUser = 725 | this.isSupportingBrowser && 726 | this.relatedApps.length < 1 && 727 | (this.hasprompt || this.isIOS); 728 | 729 | return eligibleUser; 730 | } 731 | 732 | public async install(): Promise { 733 | if (this.deferredprompt) { 734 | this.deferredprompt.prompt(); 735 | 736 | let event = new CustomEvent("show"); 737 | this.dispatchEvent(event); 738 | 739 | const choiceResult = await this.deferredprompt.userChoice; 740 | 741 | if (choiceResult.outcome === "accepted") { 742 | console.log("Your PWA has been installed"); 743 | 744 | await this.cancel(); 745 | this.installed = true; 746 | 747 | let event = new CustomEvent("hide"); 748 | this.dispatchEvent(event); 749 | 750 | return true; 751 | } else { 752 | console.log("User chose to not install your PWA"); 753 | 754 | await this.cancel(); 755 | 756 | // set installed to true because we dont 757 | // want to show the install button to 758 | // a user who chose not to install 759 | this.installed = true; 760 | 761 | let event = new CustomEvent("hide"); 762 | this.dispatchEvent(event); 763 | } 764 | } 765 | 766 | return false; 767 | } 768 | 769 | public getInstalledStatus(): boolean { 770 | // cast to any because the typescript navigator object 771 | // does not have this non standard safari object 772 | if ((navigator as any).standalone) { 773 | return (navigator as any).standalone; 774 | } else if (matchMedia("(display-mode: standalone)").matches) { 775 | return true; 776 | } else { 777 | return false; 778 | } 779 | } 780 | 781 | cancel(): Promise { 782 | return new Promise((resolve, reject) => { 783 | this.openmodal = false; 784 | 785 | if (this.hasAttribute("openmodal")) { 786 | this.removeAttribute("openmodal"); 787 | } 788 | 789 | let event = new CustomEvent("hide"); 790 | this.dispatchEvent(event); 791 | 792 | resolve(); 793 | }); 794 | } 795 | 796 | focusOut() { 797 | console.log("focus out"); 798 | } 799 | 800 | render() { 801 | return html` 802 | ${("standalone" in navigator && 803 | (navigator as any).standalone === false) || 804 | (this.usecustom !== true && 805 | this.shouldShowInstall() && 806 | this.installed === false) 807 | ? html`` 816 | : null} 817 | ${this.openmodal === true 818 | ? html` 819 | 820 | ${ 821 | this.openmodal 822 | ? html`
` 826 | : null 827 | } 828 |
829 |
830 |
831 | App Logo 834 | 835 |
836 |

${this.manifestdata.short_name || this.manifestdata.name}

837 | 838 |

839 | ${this.explainer} 840 |

841 |
842 |
843 | 844 | 850 |
851 | 852 |
853 | 854 |
855 | 856 | ${ 857 | this.manifestdata.features 858 | ? html`
859 |

${this.featuresheader}

860 |
    861 | ${ 862 | this.manifestdata.features 863 | ? this.manifestdata.features.map((feature) => { 864 | return html`
  • ${feature}
  • `; 865 | }) 866 | : null 867 | } 868 |
869 |
870 |
` 871 | : null 872 | } 873 | 874 | ${ 875 | this.manifestdata.screenshots 876 | ? html` 877 |
878 | 891 |
892 | ${this.manifestdata.screenshots.map((screen) => { 893 | return html` 894 |
895 | App Screenshot 896 |
897 | `; 898 | })} 899 |
900 | 913 |
914 | ` 915 | : null 916 | } 917 |
918 | 919 |
920 |

${this.descriptionheader}

921 |

${this.manifestdata.description}

922 |
923 |
924 | 925 | ${ 926 | !this.isIOS 927 | ? html`
928 | ${ 929 | this.deferredprompt 930 | ? html`` 936 | : html`` 942 | } 943 |
944 |
` 945 | : html`

${this.iosinstallinfotext}

` 946 | } 947 | ` 948 | : null} 949 | `; 950 | } 951 | } 952 | -------------------------------------------------------------------------------- /test/basic/basic.test.js: -------------------------------------------------------------------------------- 1 | import { expect, elementUpdated, html, fixture } from '@open-wc/testing'; 2 | 3 | import '../../dist/pwa-install.js'; 4 | 5 | it('does instantiate', async () => { 6 | const el = await fixture(''); 7 | expect(el.tagName).to.equal('PWA-INSTALL'); 8 | }); 9 | 10 | it('renders slot content correctly', async () => { 11 | const el = await fixture('Slotted'); 12 | expect(el.textContent).to.equal('Slotted'); 13 | }); 14 | 15 | it('has correct default manifest path', async () => { 16 | const el = await fixture(''); 17 | expect(el.manifestpath).to.equal('manifest.json'); 18 | }); 19 | 20 | it('handles custom manifest paths', async () => { 21 | const el = await fixture(''); 22 | expect(el.manifestpath).to.equal('custom.json'); 23 | }); 24 | 25 | it('shouldnt have manifest data', async () => { 26 | const el = await fixture(''); 27 | expect(el.manifestdata).to.be.undefined; 28 | }); 29 | 30 | it ('still shouldnt have manifest data', async () => { 31 | const el = await fixture(''); 32 | await el.getManifestData(); 33 | 34 | expect(el.manifestdata).to.be.undefined; 35 | }); 36 | 37 | it('shouldShowInstall should return false if no manifest data', async () => { 38 | const el = await fixture(''); 39 | await el.getManifestData(); 40 | 41 | const testResult = el.shouldShowInstall(); 42 | expect(testResult).to.be.false; 43 | }); 44 | 45 | it('should have manifest data', async () => { 46 | const manifestData = { 47 | short_name: "testName" 48 | }; 49 | 50 | const el = await fixture(''); 51 | el.manifestdata = manifestData; 52 | await elementUpdated(el); 53 | 54 | expect(el.manifestdata).to.not.equal(null) 55 | }); 56 | 57 | it('should detect supported browser', async () => { 58 | const el = await fixture(''); 59 | await elementUpdated(el); 60 | expect(el.isSupportingBrowser).to.be.true; 61 | }); 62 | 63 | it('shouldShowInstall should return false', async () => { 64 | const el = await fixture(''); 65 | 66 | const testResult = el.shouldShowInstall(); 67 | await elementUpdated(el); 68 | 69 | expect(testResult).to.be.false; 70 | }); 71 | 72 | it('shouldShowInstall should still return false even if showopen is true', async () => { 73 | const el = await fixture(''); 74 | 75 | const testResult = el.shouldShowInstall(); 76 | await elementUpdated(el); 77 | 78 | expect(testResult).to.be.false; 79 | }); 80 | 81 | it('shouldShowInstall should return false even if showeligible is on', async () => { 82 | const el = await fixture(''); 83 | 84 | const testResult = el.shouldShowInstall(); 85 | await elementUpdated(el); 86 | 87 | expect(testResult).to.be.false; 88 | }); 89 | 90 | it('deferredPrompt should be undefined if not in install eligible env', async () => { 91 | const el = await fixture(''); 92 | 93 | expect(el.deferredPrompt).to.be.undefined; 94 | }); 95 | 96 | it('shouldnt use ios stuff on Chromium', async () => { 97 | const el = await fixture(''); 98 | expect(el.isIOS).to.be.false; 99 | }); 100 | 101 | it('should use ios stuff when set', async () => { 102 | const el = await fixture( 103 | html` 104 | 105 | `, 106 | ); 107 | expect(el.isIOS).to.be.true; 108 | }); 109 | 110 | it('openmodal should be true when openPrompt is called', async () => { 111 | const el = await fixture(''); 112 | el.openPrompt(); 113 | 114 | expect(el.openmodal).to.be.true; 115 | }); 116 | 117 | it ('openmodal should be false once cancle is called', async () => { 118 | const el = await fixture(''); 119 | el.openPrompt(); 120 | 121 | expect(el.openmodal).to.be.true; 122 | 123 | el.cancel(); 124 | expect(el.openmodal).to.be.false; 125 | }); 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "experimentalDecorators": true, 5 | "lib": [ 6 | "dom", 7 | "es2017" 8 | ], 9 | "module": "es2015", 10 | "moduleResolution": "node", 11 | "target": "es2017", 12 | "outDir": "build", 13 | "declaration": true, 14 | "esModuleInterop": true, 15 | "sourceMap": false, 16 | "rootDir": "src", 17 | "strict": true 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ], 22 | "include": [ 23 | "src", "index.d.ts" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

PWA

17 |
18 | 19 | 20 | 21 | 24 | 25 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /www/manifest-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "pwa", 3 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum porttitor libero id hendrerit. Sed id arcu hendrerit, pellentesque ante sed, maximus odio.", 4 | "orientation": "portrait", 5 | "display": "standalone", 6 | "background_color": "#000000", 7 | "theme_color": "#000000", 8 | "start_url": "www/", 9 | "dir": "ltr", 10 | "lang": "EN", 11 | "screenshots": [ 12 | { 13 | "src": "https://cdn.glitch.com/2cd905a9-942d-41a4-86d3-7fcb2271b8bb%2FAnnotation%202019-10-22%20122534.png?v=1571772350668", 14 | "sizes": "256x256", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "https://lh3.googleusercontent.com/BIjKSdSgq8soO8LJdgH-lrnNhHhqiwH7p85M1mqUJRELrphnHSBETXAgzu7aLYE5MQ=w3240-h1932-rw", 19 | "sizes": "256x256", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "https://lh3.googleusercontent.com/BIjKSdSgq8soO8LJdgH-lrnNhHhqiwH7p85M1mqUJRELrphnHSBETXAgzu7aLYE5MQ=w3240-h1932-rw", 24 | "sizes": "256x256", 25 | "type": "image/png" 26 | } 27 | ], 28 | "features": [ 29 | "Microsoft Graph", 30 | "Low latency", 31 | "fast", 32 | "cross platform" 33 | ], 34 | "icons": [ 35 | { 36 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 37 | "sizes": "512x512", 38 | "type": "image/png" 39 | }, 40 | { 41 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 42 | "sizes": "256x256", 43 | "type": "image/png" 44 | }, 45 | { 46 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 47 | "sizes": "128x128", 48 | "type": "image/png" 49 | }, 50 | { 51 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 52 | "sizes": "64x64", 53 | "type": "image/png" 54 | }, 55 | { 56 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 57 | "sizes": "48x48", 58 | "type": "image/png" 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /www/manifest-lesser.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my PWA", 3 | "short_name": "pwa", 4 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum porttitor libero id hendrerit. Sed id arcu hendrerit, pellentesque ante sed, maximus odio.", 5 | "orientation": "portrait", 6 | "display": "standalone", 7 | "background_color": "#000000", 8 | "theme_color": "#000000", 9 | "start_url": "www/", 10 | "dir": "ltr", 11 | "lang": "EN", 12 | "icons": [ 13 | { 14 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 15 | "sizes": "512x512", 16 | "type": "image/png" 17 | }, 18 | { 19 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 20 | "sizes": "256x256", 21 | "type": "image/png" 22 | }, 23 | { 24 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 25 | "sizes": "128x128", 26 | "type": "image/png" 27 | }, 28 | { 29 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 30 | "sizes": "64x64", 31 | "type": "image/png" 32 | }, 33 | { 34 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 35 | "sizes": "48x48", 36 | "type": "image/png" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /www/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my PWA", 3 | "short_name": "pwa", 4 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum porttitor libero id hendrerit. Sed id arcu hendrerit, pellentesque ante sed, maximus odio.", 5 | "orientation": "portrait", 6 | "display": "standalone", 7 | "background_color": "#000000", 8 | "theme_color": "#000000", 9 | "start_url": "www/", 10 | "dir": "ltr", 11 | "lang": "EN", 12 | "screenshots": [ 13 | { 14 | "src": "https://cdn.glitch.com/2cd905a9-942d-41a4-86d3-7fcb2271b8bb%2FAnnotation%202019-10-22%20122534.png?v=1571772350668", 15 | "sizes": "256x256", 16 | "type": "image/png" 17 | }, 18 | { 19 | "src": "https://lh3.googleusercontent.com/BIjKSdSgq8soO8LJdgH-lrnNhHhqiwH7p85M1mqUJRELrphnHSBETXAgzu7aLYE5MQ=w3240-h1932-rw", 20 | "sizes": "256x256", 21 | "type": "image/png" 22 | }, 23 | { 24 | "src": "https://lh3.googleusercontent.com/BIjKSdSgq8soO8LJdgH-lrnNhHhqiwH7p85M1mqUJRELrphnHSBETXAgzu7aLYE5MQ=w3240-h1932-rw", 25 | "sizes": "256x256", 26 | "type": "image/png" 27 | } 28 | ], 29 | "features": [ 30 | "Microsoft Graph", 31 | "Low latency", 32 | "fast", 33 | "cross platform, this is a long feature to test. Lets add some more text." 34 | ], 35 | "icons": [ 36 | { 37 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 38 | "sizes": "512x512", 39 | "type": "image/png" 40 | }, 41 | { 42 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 43 | "sizes": "256x256", 44 | "type": "image/png" 45 | }, 46 | { 47 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 48 | "sizes": "128x128", 49 | "type": "image/png" 50 | }, 51 | { 52 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 53 | "sizes": "64x64", 54 | "type": "image/png" 55 | }, 56 | { 57 | "src": "https://cdn.glitch.com/ad1b5386-7113-477b-8f34-c42ff6178d99%2Ficon-512x512.png?v=1565728638958", 58 | "sizes": "48x48", 59 | "type": "image/png" 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /www/service-worker.js: -------------------------------------------------------------------------------- 1 | // This is the service worker with the Cache-first network 2 | 3 | const CACHE = "pwabuilder-precachev2"; 4 | const precacheFiles = [ 5 | /* Add an array of files to precache for your app */ 6 | 7 | ]; 8 | 9 | self.addEventListener("install", function (event) { 10 | console.log("[PWA Builder] Install Event processing"); 11 | 12 | console.log("[PWA Builder] Skip waiting on install"); 13 | self.skipWaiting(); 14 | 15 | event.waitUntil( 16 | caches.open(CACHE).then(function (cache) { 17 | }) 18 | ); 19 | }); 20 | 21 | // Allow sw to control of current page 22 | self.addEventListener("activate", function (event) { 23 | console.log("[PWA Builder] Claiming clients for current page"); 24 | event.waitUntil(self.clients.claim()); 25 | }); 26 | 27 | // If any fetch fails, it will look for the request in the cache and serve it from there first 28 | self.addEventListener("fetch", function (event) { 29 | 30 | }); 31 | 32 | function fromCache(request) { 33 | } 34 | 35 | function updateCache(request, response) { 36 | } 37 | -------------------------------------------------------------------------------- /www/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwa-builder/pwa-install/9b2895a539b3bedb520b23378c3a556e78f24e82/www/style.css --------------------------------------------------------------------------------