├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── add-missing-device.md │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── main.yml │ └── pull_request.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .travis.yml ├── CHANGE_LOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── angular.json ├── assets ├── iomechs-logo-dark.svg ├── iomechs-logo.svg └── logo.svg ├── commitlint.config.js ├── docs ├── classes │ ├── AppPage.html │ └── ReTree.html ├── components │ └── AppComponent.html ├── contributing.html ├── coverage.html ├── dependencies.html ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── ionicons.eot │ ├── ionicons.svg │ ├── ionicons.ttf │ ├── ionicons.woff │ ├── ionicons.woff2 │ ├── roboto-v15-latin-300.eot │ ├── roboto-v15-latin-300.svg │ ├── roboto-v15-latin-300.ttf │ ├── roboto-v15-latin-300.woff │ ├── roboto-v15-latin-300.woff2 │ ├── roboto-v15-latin-700.eot │ ├── roboto-v15-latin-700.svg │ ├── roboto-v15-latin-700.ttf │ ├── roboto-v15-latin-700.woff │ ├── roboto-v15-latin-700.woff2 │ ├── roboto-v15-latin-italic.eot │ ├── roboto-v15-latin-italic.svg │ ├── roboto-v15-latin-italic.ttf │ ├── roboto-v15-latin-italic.woff │ ├── roboto-v15-latin-italic.woff2 │ ├── roboto-v15-latin-regular.eot │ ├── roboto-v15-latin-regular.svg │ ├── roboto-v15-latin-regular.ttf │ ├── roboto-v15-latin-regular.woff │ └── roboto-v15-latin-regular.woff2 ├── graph │ └── dependencies.svg ├── images │ ├── compodoc-vectorise-inverted.png │ ├── compodoc-vectorise-inverted.svg │ ├── compodoc-vectorise.png │ ├── compodoc-vectorise.svg │ ├── coverage-badge-documentation.svg │ ├── coverage-badge.svg │ └── favicon.ico ├── index.html ├── injectables │ ├── DeviceDetectorService.html │ └── UniversalDeviceDetectorService.html ├── interfaces │ └── DeviceInfo.html ├── js │ ├── compodoc.js │ ├── lazy-load-graphs.js │ ├── libs │ │ ├── EventDispatcher.js │ │ ├── bootstrap-native.js │ │ ├── clipboard.min.js │ │ ├── custom-elements-es5-adapter.js │ │ ├── custom-elements.min.js │ │ ├── d3.v3.min.js │ │ ├── deep-iterator.js │ │ ├── es6-shim.min.js │ │ ├── htmlparser.js │ │ ├── innersvg.js │ │ ├── lit-html.js │ │ ├── prism.js │ │ ├── promise.min.js │ │ ├── svg-pan-zoom.min.js │ │ ├── tablesort.min.js │ │ ├── tablesort.number.min.js │ │ ├── vis.min.js │ │ └── zepto.min.js │ ├── menu-wc.js │ ├── menu-wc_es5.js │ ├── menu.js │ ├── routes.js │ ├── search │ │ ├── lunr.min.js │ │ ├── search-lunr.js │ │ ├── search.js │ │ └── search_index.js │ ├── sourceCode.js │ ├── svg-pan-zoom.controls.js │ ├── tabs.js │ └── tree.js ├── license.html ├── miscellaneous │ ├── enumerations.html │ ├── functions.html │ └── variables.html ├── modules.html ├── modules │ ├── AppModule.html │ ├── AppModule │ │ └── dependencies.svg │ ├── AppServerModule.html │ ├── AppServerModule │ │ └── dependencies.svg │ └── DeviceDetectorModule.html ├── overview.html ├── pipes │ └── KeysPipe.html ├── properties.html └── styles │ ├── bootstrap-card.css │ ├── bootstrap.min.css │ ├── compodoc.css │ ├── dark.css │ ├── font-awesome.min.css │ ├── ionicons.min.css │ ├── laravel.css │ ├── material.css │ ├── original.css │ ├── postmark.css │ ├── prism.css │ ├── readthedocs.css │ ├── reset.css │ ├── stripe.css │ ├── style.css │ ├── tablesort.css │ └── vagrant.css ├── jestSetup.ts ├── package-lock.json ├── package.json ├── projects ├── demo │ ├── e2e │ │ ├── protractor.conf.js │ │ ├── src │ │ │ ├── app.e2e-spec.ts │ │ │ └── app.po.ts │ │ └── tsconfig.json │ ├── server.ts │ ├── server │ │ ├── Procfile │ │ └── package.json │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.config.server.ts │ │ │ ├── app.config.ts │ │ │ └── pipes │ │ │ │ ├── keys.pipe.spec.ts │ │ │ │ └── keys.pipe.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ └── images │ │ │ │ └── github-logo.png │ │ ├── express.tokens.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.server.ts │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── server.ts │ │ └── styles.scss │ ├── tsconfig.app.json │ └── tslint.json └── ngx-device-detector │ ├── README.md │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── device-detector.constants.ts │ │ ├── device-detector.service.spec.ts │ │ ├── device-detector.service.ts │ │ └── retree.ts │ └── public-api.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ └── tslint.json ├── scripts ├── bump-version.js └── deploy_ssr_demo.sh ├── steps-to-release.md ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ahsanayaz 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/add-missing-device.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Add Missing Device 3 | about: Request to add a missing device not being detected by the library 4 | title: "[MISSING DEVICE]" 5 | labels: add device to regexes 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Device User Agent 11 | 12 | 13 | ## Expected Output 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 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/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2.3.4 12 | - name: Use Node.js 13 | uses: actions/setup-node@v2 14 | with: 15 | node-version: '22' 16 | - name: Install dependencies 17 | run: npm install --legacy-peer-deps 18 | - name: Lint and Test 19 | run: npm run test && npm run lint 20 | - name: Build 21 | run: npm run build:lib && npm run build:ci && cp -R dist/demo/browser ./docs/demo 22 | - name: GitHub Pages action 23 | uses: peaceiris/actions-gh-pages@v3.9.3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ./docs 27 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Lint and Build 2 | on: 3 | pull_request: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2.3.4 12 | - name: Use Node.js 13 | uses: actions/setup-node@v2 14 | with: 15 | node-version: '22' 16 | - name: Install dependencies 17 | run: npm install --legacy-peer-deps 18 | - name: Lint and Test 19 | run: npm run test && npm run lint 20 | - name: Build 21 | run: npm run build:lib && npm run build:ci 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # editors 4 | .vscode 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | # Only exists if Bazel was run 11 | /bazel-out 12 | 13 | # dependencies 14 | /node_modules 15 | 16 | # profiling files 17 | chrome-profiler-events*.json 18 | speed-measure-plugin*.json 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | .history/* 36 | 37 | # misc 38 | /.angular/cache 39 | /.sass-cache 40 | /connect.lock 41 | /coverage 42 | /libpeerconnection.log 43 | npm-debug.log 44 | yarn-error.log 45 | testem.log 46 | /typings 47 | 48 | # System Files 49 | .DS_Store 50 | Thumbs.db 51 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Node 2 | node_modules/* 3 | npm-debug.log 4 | docs/* 5 | # DO NOT IGNORE TYPESCRIPT FILES FOR NPM 6 | # TypeScript 7 | # *.js 8 | # *.map 9 | # *.d.ts 10 | 11 | # JetBrains 12 | .idea 13 | .project 14 | .settings 15 | .idea/* 16 | *.iml 17 | 18 | # VS Code 19 | .vscode/* 20 | 21 | # Windows 22 | Thumbs.db 23 | Desktop.ini 24 | 25 | # Mac 26 | .DS_Store 27 | **/.DS_Store 28 | 29 | # Ngc generated files 30 | **/*.ngfactory.ts 31 | 32 | # Library files 33 | src/* 34 | build/* 35 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "singleQuote": true, 4 | "semi": true, 5 | "bracketSpacing": true, 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: true 3 | node_js: 4 | - 16 5 | 6 | demo/dist: xenial 7 | 8 | branches: 9 | only: 10 | - master 11 | 12 | services: 13 | - xvfb 14 | 15 | script: 16 | - npm run lint 17 | - npm install 18 | - npm run test 19 | - node_modules/.bin/webdriver-manager update 20 | - npm run build:ci 21 | - cp -R dist/demo/browser ./docs/demo 22 | 23 | deploy: 24 | provider: pages 25 | skip_cleanup: true 26 | github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard 27 | local_dir: docs 28 | on: 29 | branch: master 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to Contribute 2 | 3 | There are many ways you can Contribute to this project! 4 | - Create issues or help close them 5 | - Fork, Enhance, Fix Bugs, and send in Pull Requests 6 | - Help write Tests or improve Examples / Documentation 7 | - Share repo with others. 8 | 9 | ### Issues 10 | Found a bug, have some feedback or really want some feature? Lock in an Issue. Here are some tips: 11 | 12 | - Make sure the bug you're reporting really is a bug and you have read the docs/readme. 13 | - Provide as many details as you can about the bug. How can it be reproduced? What have you tried so far? 14 | - For feature requests, really explain why your feature is important and how it improves the library and helps the developers using it. 15 | - Go through existing issues. Make sure that it hasn't already been opened. 16 | - If planning on helping close an issue, don't be shy. Ask questions and get more information before sending your PR. 17 | ### Pull Requests 18 | 19 | Want to fix a bug, implement a feature or help with the tests / docs? Please read this first: 20 | 21 | - Make sure there's an open issue for what you're sending in your Pull Request. If not, open it. 22 | - Send in your PRs agains the `master` branch. 23 | - Write unit tests / docs for any new feature you implement. 24 | - Make sure your PRs pass all tests and CI. 25 | - Be active in your relevant issue and PR threads. 26 | - Make sure no one else is assigned or working on the issue. It's a good idea to leave a comment stating that you're going to implement it before sending in your PR. 27 | - This way no two devs are working on the same thing. 28 | - If required, update the CHANGELOG.md file with a small summary of your changes in the latest version (add a new section if needed) 29 | 30 | 31 | ### Finally, Thank you! 32 | 33 | It means a lot that you took time out to improve this package and contribute back to the community. 🎉 34 | 35 | ### Relevant Links 36 | 37 | [Project Page](https://github.com/AhsanAyaz/ngx-device-detector) 38 | 39 | [Author](https://github.com/AhsanAyaz) 40 | 41 | [NPM Package](https://www.npmjs.com/package/ngx-device-detector) 42 | 43 | [License](https://github.com/AhsanAyaz/ngx-device-detector/blob/master/LICENSE) 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Muhammad Ahsan Ayaz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | 6 |

ngx-device-detector

7 |
8 | 9 |

10 | An Angular 6+ powered AOT compatible device detector that helps to identify browser, os and other useful information regarding the device using the app. The processing is based on user-agent. This library was built at KoderLabs, which is one of the best places I've worked at ❤️ 11 |

12 | 13 |

14 | build status 15 | npm version 16 | license 17 | stars 18 | 19 |

20 |

21 | Deprecated package : 22 | npm downloads total 23 | npm downloads/month 24 |

25 |

26 | New package : 27 | npm downloads total 28 | npm downloads/month

29 | If you use Angular 5, you must use v1.5.2 or earlier 30 |

31 | 32 | ## DOCS 33 | 34 | [Ngx Device Detector DOCS](https://ahsanayaz.github.io/ngx-device-detector) 35 | 36 | ## Live DEMO 37 | 38 | [Regular Demo](https://ahsanayaz.github.io/ngx-device-detector/demo) 39 | 40 | 41 | 42 | ## Dependencies 43 | 44 | Latest version available for each version of Angular 45 | 46 | | ngx-device-detector | Angular | 47 | | ------------------- | ------- | 48 | | 1.3.3 | 7.x | 49 | | 1.3.5 | 8.x | 50 | | 1.4.1 | 9.x | 51 | | 1.4.5 | 10.x | 52 | | 2.0.5 | 11.x | 53 | | 2.1.0 | 12.x | 54 | | 3.x.x | 13.x | 55 | | 4.x.x | 14.x | 56 | | 5.x.x | 15.x | 57 | | 6.x.x | 16.x | 58 | | 7.x.x | 17.x | 59 | | 8.x.x | 18.x | 60 | | 9.x.x | 19.x | 61 | | 10.x.x | 20.x | 62 | 63 | ## Installation 64 | 65 | To install this library, run: 66 | 67 | ```bash 68 | $ npm install ngx-device-detector --save 69 | ``` 70 | 71 | In your component where you want to use the Device Service 72 | 73 | ```typescript 74 | import { Component } from '@angular/core'; 75 | ... 76 | import { DeviceDetectorService } from 'ngx-device-detector'; 77 | ... 78 | @Component({ 79 | selector: 'home', // 80 | styleUrls: [ './home.component.scss' ], 81 | templateUrl: './home.component.html', 82 | ... 83 | }) 84 | 85 | export class HomeComponent { 86 | deviceInfo = null; 87 | ... 88 | constructor(..., private http: Http, private deviceService: DeviceDetectorService) { 89 | this.epicFunction(); 90 | } 91 | ... 92 | epicFunction() { 93 | console.log('hello `Home` component'); 94 | this.deviceInfo = this.deviceService.getDeviceInfo(); 95 | const isMobile = this.deviceService.isMobile(); 96 | const isTablet = this.deviceService.isTablet(); 97 | const isDesktopDevice = this.deviceService.isDesktop(); 98 | const isDesktopMode = this.deviceService.isDesktopModeEnabled(); 99 | console.log(this.deviceInfo); 100 | console.log(isMobile); // returns if the device is a mobile device (android / iPhone / windows-phone etc) 101 | console.log(isTablet); // returns if the device us a tablet (iPad etc) 102 | console.log(isDesktopDevice); // returns if the app is running on a Desktop browser. 103 | console.log(isDesktopMode); // returns if a mobile device is running in desktop mode 104 | } 105 | ... 106 | } 107 | 108 | ``` 109 | 110 | For SSR, you have to make sure that the User Agent is available for device detection. I.e. you'll need to provide it manually. If using ExpressJS for example: 111 | 112 | **express.tokens.ts** 113 | 114 | ```typescript 115 | import { InjectionToken } from '@angular/core'; 116 | import { Request, Response } from 'express'; 117 | 118 | export const REQUEST = new InjectionToken('REQUEST'); 119 | export const RESPONSE = new InjectionToken('RESPONSE'); 120 | ``` 121 | 122 | **universal-device-detector.service.ts:** 123 | 124 | ```typescript 125 | import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core'; 126 | import { REQUEST } from 'path/to/express.tokens'; 127 | import { Request } from 'express'; 128 | import { DeviceDetectorService } from 'ngx-device-detector'; 129 | import { isPlatformServer } from '@angular/common'; 130 | 131 | @Injectable() 132 | export class UniversalDeviceDetectorService extends DeviceDetectorService { 133 | constructor(@Inject(PLATFORM_ID) platformId: any, @Optional() @Inject(REQUEST) request: Request) { 134 | super(platformId); 135 | if (isPlatformServer(platformId)) { 136 | super.setDeviceInfo((request.headers['user-agent'] as string) || ''); 137 | } 138 | } 139 | } 140 | ``` 141 | 142 | **app.server.module.ts:** 143 | 144 | ```typescript 145 | { 146 | provide: DeviceDetectorService, 147 | useClass: UniversalDeviceDetectorService 148 | }, 149 | ``` 150 | 151 | ## Device service 152 | 153 | Holds the following properties 154 | 155 | - browser 156 | - os 157 | - device 158 | - userAgent 159 | - os_version 160 | - isDesktopMode 161 | 162 | ## Helper Methods 163 | 164 | - **isMobile() :** returns if the device is a mobile device (android / iPhone/ windows-phone etc) 165 | - **isTablet() :** returns if the device us a tablet (iPad etc) 166 | - **isDesktop() :** returns if the app is running on a Desktop browser. 167 | - **isDesktopModeEnabled() :** returns if a mobile device is running in desktop mode (mobile browsers requesting desktop websites) 168 | 169 | ## Development 170 | 171 | To generate all `*.js`, `*.js.map` and `*.d.ts` files: 172 | 173 | ```bash 174 | $ npm run tsc 175 | ``` 176 | 177 | To lint all `*.ts` files: 178 | 179 | ```bash 180 | $ npm run lint 181 | ``` 182 | 183 | To run unit tests 184 | 185 | ```bash 186 | $ npm run test 187 | ``` 188 | 189 | To build the library 190 | 191 | ```bash 192 | $ npm run build 193 | ``` 194 | 195 | ## Run the DEMO 196 | 197 | Make sure you have @angular/cli installed 198 | 199 | ```bash 200 | $ npm install -g @angular/cli 201 | ``` 202 | 203 | ```bash 204 | $ cd demo 205 | $ npm install 206 | $ ng serve 207 | ``` 208 | 209 | the demo will be up at `localhost:4200` 210 | 211 | ## Change Log 212 | 213 | Please see [CHANGE_LOG.md](CHANGE_LOG.md) for the updates. 214 | 215 | ## Credits 216 | 217 | The library is inspired by and based on the work from [ng-device-detector ](https://github.com/srfrnk/ng-device-detector). Also used a typescript wrapper of the amazing work in [ReTree](https://github.com/srfrnk/re-tree) for regex based needs and an Angular2 Library Creator boilerplate to get the work started fast. I.e. [Generator Angular2 library](https://github.com/jvandemo/generator-angular2-library). 218 | 219 | ## License 220 | 221 | [MIT](https://github.com/AhsanAyaz/ngx-device-detector/blob/master/LICENSE) 222 | 223 | ## Need help with your Angular/Web projects 224 | 225 | Write to us at [IOMechs](mailto:info@iomechs.com) 226 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-device-detector": { 7 | "projectType": "library", 8 | "root": "projects/ngx-device-detector", 9 | "sourceRoot": "projects/ngx-device-detector/src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "tsConfig": "projects/ngx-device-detector/tsconfig.lib.json", 16 | "project": "projects/ngx-device-detector/ng-package.json" 17 | }, 18 | "configurations": { 19 | "production": { 20 | "tsConfig": "projects/ngx-device-detector/tsconfig.lib.prod.json" 21 | } 22 | } 23 | } 24 | } 25 | }, 26 | "demo": { 27 | "projectType": "application", 28 | "schematics": { 29 | "@schematics/angular:component": { 30 | "style": "scss" 31 | } 32 | }, 33 | "root": "projects/demo", 34 | "sourceRoot": "projects/demo/src", 35 | "prefix": "app", 36 | "architect": { 37 | "build": { 38 | "builder": "@angular-devkit/build-angular:application", 39 | "options": { 40 | "outputPath": { 41 | "base": "dist/demo" 42 | }, 43 | "index": "projects/demo/src/index.html", 44 | "polyfills": ["projects/demo/src/polyfills.ts"], 45 | "tsConfig": "projects/demo/tsconfig.app.json", 46 | "assets": ["projects/demo/src/favicon.ico", "projects/demo/src/assets"], 47 | "styles": ["projects/demo/src/styles.scss"], 48 | "scripts": [], 49 | "extractLicenses": false, 50 | "sourceMap": true, 51 | "optimization": false, 52 | "namedChunks": true, 53 | "browser": "projects/demo/src/main.ts", 54 | "server": "projects/demo/src/main.server.ts", 55 | "prerender": true, 56 | "ssr": { 57 | "entry": "projects/demo/src/server.ts" 58 | } 59 | }, 60 | "configurations": { 61 | "production": { 62 | "optimization": true, 63 | "outputHashing": "all", 64 | "sourceMap": false, 65 | "namedChunks": false, 66 | "extractLicenses": true, 67 | "budgets": [ 68 | { 69 | "type": "initial", 70 | "maximumWarning": "2mb", 71 | "maximumError": "5mb" 72 | }, 73 | { 74 | "type": "anyComponentStyle", 75 | "maximumWarning": "6kb", 76 | "maximumError": "10kb" 77 | } 78 | ] 79 | } 80 | } 81 | }, 82 | "serve": { 83 | "builder": "@angular-devkit/build-angular:dev-server", 84 | "options": { 85 | "buildTarget": "demo:build" 86 | }, 87 | "configurations": { 88 | "production": { 89 | "buildTarget": "demo:build:production" 90 | } 91 | } 92 | }, 93 | "extract-i18n": { 94 | "builder": "@angular-devkit/build-angular:extract-i18n", 95 | "options": { 96 | "buildTarget": "demo:build" 97 | } 98 | }, 99 | "e2e": { 100 | "builder": "@angular-devkit/build-angular:protractor", 101 | "options": { 102 | "protractorConfig": "projects/demo/e2e/protractor.conf.js", 103 | "devServerTarget": "demo:serve" 104 | }, 105 | "configurations": { 106 | "production": { 107 | "devServerTarget": "demo:serve:production" 108 | } 109 | } 110 | } 111 | } 112 | } 113 | }, 114 | "cli": { 115 | "analytics": "16a03793-8b3c-4c78-a787-e82f61ef71be" 116 | }, 117 | "schematics": { 118 | "@schematics/angular:component": { 119 | "type": "component" 120 | }, 121 | "@schematics/angular:directive": { 122 | "type": "directive" 123 | }, 124 | "@schematics/angular:service": { 125 | "type": "service" 126 | }, 127 | "@schematics/angular:guard": { 128 | "typeSeparator": "." 129 | }, 130 | "@schematics/angular:interceptor": { 131 | "typeSeparator": "." 132 | }, 133 | "@schematics/angular:module": { 134 | "typeSeparator": "." 135 | }, 136 | "@schematics/angular:pipe": { 137 | "typeSeparator": "." 138 | }, 139 | "@schematics/angular:resolver": { 140 | "typeSeparator": "." 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'type-enum': [ 4 | 2, 5 | 'always', 6 | [ 7 | 'build', 8 | 'chore', 9 | 'ci', 10 | 'docs', 11 | 'feat', 12 | 'fix', 13 | 'perf', 14 | 'refactor', 15 | 'revert', 16 | 'style', 17 | 'test', 18 | ], 19 | ], 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /docs/contributing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 65 | 66 | 72 | 73 | 76 | 77 |
78 |
79 | 82 | 83 |
84 |
85 | 86 |

How to Contribute

87 |

There are many ways you can Contribute to this project!

88 |
    89 |
  • Create issues or help close them
  • 90 |
  • Fork, Enhance, Fix Bugs, and send in Pull Requests
  • 91 |
  • Help write Tests or improve Examples / Documentation
  • 92 |
  • Share repo with others.
  • 93 |
94 |

Issues

95 |

Found a bug, have some feedback or really want some feature? Lock in an Issue. Here are some tips:

96 |
    97 |
  • Make sure the bug you're reporting really is a bug and you have read the docs/readme.
  • 98 |
  • Provide as many details as you can about the bug. How can it be reproduced? What have you tried so far?
  • 99 |
  • For feature requests, really explain why your feature is important and how it improves the library and helps the developers using it.
  • 100 |
  • Go through existing issues. Make sure that it hasn't already been opened.
  • 101 |
  • If planning on helping close an issue, don't be shy. Ask questions and get more information before sending your PR.
  • 102 |
103 |

Pull Requests

104 |

Want to fix a bug, implement a feature or help with the tests / docs? Please read this first:

105 |
    106 |
  • Make sure there's an open issue for what you're sending in your Pull Request. If not, open it.
  • 107 |
  • Send in your PRs agains the master branch.
  • 108 |
  • Write unit tests / docs for any new feature you implement.
  • 109 |
  • Make sure your PRs pass all tests and CI.
  • 110 |
  • Be active in your relevant issue and PR threads.
  • 111 |
  • Make sure no one else is assigned or working on the issue. It's a good idea to leave a comment stating that you're going to implement it before sending in your PR.
  • 112 |
  • This way no two devs are working on the same thing.
  • 113 |
  • If required, update the CHANGELOG.md file with a small summary of your changes in the latest version (add a new section if needed)
  • 114 |
115 |

Finally, Thank you!

116 |

It means a lot that you took time out to improve this package and contribute back to the community. 🎉

117 |

Relevant Links

118 |

Project Page

119 |

Author

120 |

NPM Package

121 |

License

122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 |
144 |
145 |

results matching ""

146 |
    147 |
    148 |
    149 |

    No results matching ""

    150 |
    151 |
    152 |
    153 | 154 |
    155 |
    156 | 157 | 165 | 166 | 172 | 173 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /docs/dependencies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 65 | 66 | 72 | 73 | 76 | 77 |
    78 |
    79 | 82 | 83 |
    84 |
    85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 103 |
      104 |
    • 105 | @angular/animations : ~20.0.0
    • 106 |
    • 107 | @angular/common : ~20.0.0
    • 108 |
    • 109 | @angular/compiler : ~20.0.0
    • 110 |
    • 111 | @angular/core : ~20.0.0
    • 112 |
    • 113 | @angular/forms : ~20.0.0
    • 114 |
    • 115 | @angular/platform-browser : ~20.0.0
    • 116 |
    • 117 | @angular/platform-browser-dynamic : ~20.0.0
    • 118 |
    • 119 | @angular/platform-server : ~20.0.0
    • 120 |
    • 121 | @angular/router : ~20.0.0
    • 122 |
    • 123 | @angular/ssr : ^20.0.0
    • 124 |
    • 125 | conventional-changelog-cli : ~5.0.0
    • 126 |
    • 127 | express : ^4.18.2
    • 128 |
    • 129 | jest-preset-angular : ^14.5.5
    • 130 |
    • 131 | ngx-device-detector : ^8.0.0
    • 132 |
    • 133 | rxjs : ~7.8.1
    • 134 |
    • 135 | tslib : ~2.6.3
    • 136 |
    • 137 | zone.js : ~0.15.0
    • 138 |
    139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
    147 |
    148 |

    results matching ""

    149 |
      150 |
      151 |
      152 |

      No results matching ""

      153 |
      154 |
      155 |
      156 | 157 |
      158 |
      159 | 160 | 168 | 169 | 175 | 176 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /docs/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/ionicons.eot -------------------------------------------------------------------------------- /docs/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/ionicons.ttf -------------------------------------------------------------------------------- /docs/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/ionicons.woff -------------------------------------------------------------------------------- /docs/fonts/ionicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/ionicons.woff2 -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-300.eot -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-300.ttf -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-300.woff -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-300.woff2 -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-700.eot -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-700.ttf -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-700.woff -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-700.woff2 -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-italic.eot -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-italic.ttf -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-italic.woff -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-italic.woff2 -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-regular.eot -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-regular.woff -------------------------------------------------------------------------------- /docs/fonts/roboto-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/fonts/roboto-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /docs/graph/dependencies.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | dependencies 11 | 12 | Legend 13 | 14 |  Declarations 15 | 16 |  Module 17 | 18 |  Bootstrap 19 | 20 |  Providers 21 | 22 |  Exports 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/images/compodoc-vectorise-inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/images/compodoc-vectorise-inverted.png -------------------------------------------------------------------------------- /docs/images/compodoc-vectorise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/images/compodoc-vectorise.png -------------------------------------------------------------------------------- /docs/images/coverage-badge-documentation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | documentation 7 | 4% 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/images/coverage-badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | documentation 7 | 8% 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/docs/images/favicon.ico -------------------------------------------------------------------------------- /docs/js/compodoc.js: -------------------------------------------------------------------------------- 1 | var compodoc = { 2 | EVENTS: { 3 | READY: 'compodoc.ready', 4 | SEARCH_READY: 'compodoc.search.ready' 5 | } 6 | }; 7 | 8 | Object.assign( compodoc, EventDispatcher.prototype ); 9 | 10 | document.addEventListener('DOMContentLoaded', function() { 11 | compodoc.dispatchEvent({ 12 | type: compodoc.EVENTS.READY 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /docs/js/lazy-load-graphs.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var lazyGraphs = [].slice.call(document.querySelectorAll('[lazy]')); 3 | var active = false; 4 | 5 | var lazyLoad = function() { 6 | if (active === false) { 7 | active = true; 8 | 9 | setTimeout(function() { 10 | lazyGraphs.forEach(function(lazyGraph) { 11 | if ( 12 | lazyGraph.getBoundingClientRect().top <= window.innerHeight && 13 | lazyGraph.getBoundingClientRect().bottom >= 0 && 14 | getComputedStyle(lazyGraph).display !== 'none' 15 | ) { 16 | lazyGraph.data = lazyGraph.getAttribute('lazy'); 17 | lazyGraph.removeAttribute('lazy'); 18 | 19 | lazyGraphs = lazyGraphs.filter(function(image) { return image !== lazyGraph}); 20 | 21 | if (lazyGraphs.length === 0) { 22 | document.removeEventListener('scroll', lazyLoad); 23 | window.removeEventListener('resize', lazyLoad); 24 | window.removeEventListener('orientationchange', lazyLoad); 25 | } 26 | } 27 | }); 28 | 29 | active = false; 30 | }, 200); 31 | } 32 | }; 33 | 34 | // initial load 35 | lazyLoad(); 36 | 37 | var container = document.querySelector('.container-fluid.modules'); 38 | if (container) { 39 | container.addEventListener('scroll', lazyLoad); 40 | window.addEventListener('resize', lazyLoad); 41 | window.addEventListener('orientationchange', lazyLoad); 42 | } 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /docs/js/libs/EventDispatcher.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | var EventDispatcher=function(){};Object.assign(EventDispatcher.prototype,{addEventListener:function(i,t){void 0===this._listeners&&(this._listeners={});var e=this._listeners;void 0===e[i]&&(e[i]=[]),-1===e[i].indexOf(t)&&e[i].push(t)},hasEventListener:function(i,t){if(void 0===this._listeners)return!1;var e=this._listeners;return void 0!==e[i]&&-1!==e[i].indexOf(t)},removeEventListener:function(i,t){if(void 0!==this._listeners){var e=this._listeners[i];if(void 0!==e){var s=e.indexOf(t);-1!==s&&e.splice(s,1)}}},dispatchEvent:function(i){if(void 0!==this._listeners){var t=this._listeners[i.type];if(void 0!==t){i.target=this;var e=[],s=0,n=t.length;for(s=0;s",">"));else if(1==i){if(r.push("<",e.tagName),e.hasAttributes())for(var n=e.attributes,s=0,o=n.length;s");for(var h=e.childNodes,s=0,o=h.length;s")}else r.push("/>")}else{if(8!=i)throw"Error serializing XML. Unhandled node of type: "+i;r.push("\x3c!--",e.nodeValue,"--\x3e")}};Object.defineProperty(e.prototype,"innerHTML",{get:function(){for(var e=[],r=this.firstChild;r;)t(r,e),r=r.nextSibling;return e.join("")},set:function(e){for(;this.firstChild;)this.removeChild(this.firstChild);try{var t=new DOMParser;t.async=!1,sXML=""+e+"";for(var r=t.parseFromString(sXML,"text/xml").documentElement.firstChild;r;)this.appendChild(this.ownerDocument.importNode(r,!0)),r=r.nextSibling}catch(e){throw new Error("Error parsing XML string")}}})}}((0,eval)("this").SVGElement); -------------------------------------------------------------------------------- /docs/js/libs/promise.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2013 (c) Pierre Duquesne 3 | * Licensed under the New BSD License. 4 | * https://github.com/stackp/promisejs 5 | */ 6 | (function(a){function b(){this._callbacks=[];}b.prototype.then=function(a,c){var d;if(this._isdone)d=a.apply(c,this.result);else{d=new b();this._callbacks.push(function(){var b=a.apply(c,arguments);if(b&&typeof b.then==='function')b.then(d.done,d);});}return d;};b.prototype.done=function(){this.result=arguments;this._isdone=true;for(var a=0;a=300)&&j.status!==304);h.done(a,j.responseText,j);}};j.send(k);return h;}function h(a){return function(b,c,d){return g(a,b,c,d);};}var i={Promise:b,join:c,chain:d,ajax:g,get:h('GET'),post:h('POST'),put:h('PUT'),del:h('DELETE'),ENOXHR:1,ETIMEOUT:2,ajaxTimeout:0};if(typeof define==='function'&&define.amd)define(function(){return i;});else a.promise=i;})(this); -------------------------------------------------------------------------------- /docs/js/libs/tablesort.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * tablesort v5.2.1 (2021-10-30) 3 | * http://tristen.ca/tablesort/demo/ 4 | * Copyright (c) 2021 ; Licensed MIT 5 | */ 6 | !function(){function a(b,c){if(!(this instanceof a))return new a(b,c);if(!b||"TABLE"!==b.tagName)throw new Error("Element must be a table");this.init(b,c||{})}var b=[],c=function(a){var b;return window.CustomEvent&&"function"==typeof window.CustomEvent?b=new CustomEvent(a):(b=document.createEvent("CustomEvent"),b.initCustomEvent(a,!1,!1,void 0)),b},d=function(a,b){return a.getAttribute(b.sortAttribute||"data-sort")||a.textContent||a.innerText||""},e=function(a,b){return a=a.trim().toLowerCase(),b=b.trim().toLowerCase(),a===b?0:a0)if(a.tHead&&a.tHead.rows.length>0){for(e=0;e0&&n.push(m),o++;if(!n)return}for(o=0;o 16 | 149 | 150 | `); 151 | this.innerHTML = tp.strings; 152 | } 153 | }); -------------------------------------------------------------------------------- /docs/js/search/search-lunr.js: -------------------------------------------------------------------------------- 1 | (function(compodoc) { 2 | 3 | function LunrSearchEngine() { 4 | this.index = undefined; 5 | this.store = {}; 6 | this.name = 'LunrSearchEngine'; 7 | } 8 | 9 | LunrSearchEngine.prototype.init = function() { 10 | var that = this, 11 | d = new promise.Promise(); 12 | 13 | that.index = lunr.Index.load(COMPODOC_SEARCH_INDEX.index); 14 | that.store = COMPODOC_SEARCH_INDEX.store; 15 | d.done(); 16 | 17 | return d; 18 | }; 19 | 20 | LunrSearchEngine.prototype.search = function(q, offset, length) { 21 | var that = this, 22 | results = [], 23 | d = new promise.Promise(); 24 | 25 | if (this.index) { 26 | results = $.map(this.index.search('*' + q + '*'), function(result) { 27 | var doc = that.store[result.ref]; 28 | 29 | return { 30 | title: doc.title, 31 | url: doc.url, 32 | body: doc.summary || doc.body 33 | }; 34 | }); 35 | } 36 | 37 | d.done({ 38 | query: q, 39 | results: length === 0 ? results : results.slice(0, length), 40 | count: results.length 41 | }); 42 | 43 | return d; 44 | }; 45 | 46 | compodoc.addEventListener(compodoc.EVENTS.READY, function(event) { 47 | var engine = new LunrSearchEngine(), 48 | initialized = false; 49 | 50 | function query(q, offset, length) { 51 | if (!initialized) throw new Error('Search has not been initialized'); 52 | return engine.search(q, offset, length); 53 | } 54 | 55 | compodoc.search = { 56 | query: query 57 | }; 58 | 59 | engine.init() 60 | .then(function() { 61 | initialized = true; 62 | compodoc.dispatchEvent({ 63 | type: compodoc.EVENTS.SEARCH_READY 64 | }); 65 | }); 66 | }); 67 | })(compodoc); 68 | -------------------------------------------------------------------------------- /docs/js/sourceCode.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var $tabSource = document.querySelector('#source-tab'), 3 | $tabInfo = document.querySelector('#info-tab'), 4 | $tabReadme = document.querySelector('#readme-tab'), 5 | $tabTemplate = document.querySelector('#templateData-tab'), 6 | $tabTree = document.querySelector('#tree-tab'), 7 | $tabExample = document.querySelector('#example-tab'), 8 | $prismPre = document.querySelector('pre.compodoc-sourcecode'); 9 | if ($tabSource && $prismPre) { 10 | $prismCode = $prismPre.querySelector('code'), 11 | $content = document.querySelector('.content'), 12 | prismLinks = document.querySelectorAll('.link-to-prism') 13 | 14 | for (var i = 0; i < prismLinks.length; i++) { 15 | prismLinks[i].addEventListener('click', linkToPrism, false); 16 | } 17 | 18 | function linkToPrism(event) { 19 | var targetLine = event.target.getAttribute('data-line'); 20 | event.preventDefault(); 21 | 22 | $prismPre.setAttribute('data-line', targetLine); 23 | Prism.highlightElement($prismCode, function() {}); 24 | 25 | $tabSource.click(); 26 | 27 | setTimeout(function() { 28 | var $prismHighlightLine = document.querySelector('.line-highlight'), 29 | top = parseInt(getComputedStyle($prismHighlightLine)['top']); 30 | $content.scrollTop = top; 31 | }, 500); 32 | }; 33 | 34 | window.onhashchange = function(event) { 35 | switch (window.location.hash) { 36 | case '': 37 | case '#info': 38 | $tabInfo.click(); 39 | break; 40 | case '#readme': 41 | $tabReadme.click(); 42 | break; 43 | case '#source': 44 | $tabSource.click(); 45 | break; 46 | case '#template': 47 | $tabTemplate.click(); 48 | break; 49 | case '#dom-tree': 50 | $tabTree.click(); 51 | break; 52 | case '#example': 53 | $tabExample.click(); 54 | break; 55 | } 56 | } 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /docs/js/svg-pan-zoom.controls.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | if (document.getElementById('module-graph-svg')) { 3 | panZoom = svgPanZoom(document.getElementById('module-graph-svg').querySelector('svg'), { 4 | zoomEnabled: true, 5 | minZoom: 1, 6 | maxZoom: 5 7 | }); 8 | 9 | document.getElementById('zoom-in').addEventListener('click', function(ev) { 10 | ev.preventDefault(); 11 | panZoom.zoomIn(); 12 | }); 13 | 14 | document.getElementById('zoom-out').addEventListener('click', function(ev) { 15 | ev.preventDefault(); 16 | panZoom.zoomOut(); 17 | }); 18 | 19 | document.getElementById('reset').addEventListener('click', function(ev) { 20 | ev.preventDefault(); 21 | panZoom.resetZoom(); 22 | panZoom.resetPan(); 23 | }); 24 | 25 | var overviewFullscreen = false, 26 | originalOverviewHeight; 27 | 28 | document.getElementById('fullscreen').addEventListener('click', function(ev) { 29 | if (overviewFullscreen) { 30 | document.getElementById('module-graph-svg').style.height = originalOverviewHeight; 31 | overviewFullscreen = false; 32 | if (ev.target) { 33 | ev.target.classList.remove('ion-md-close'); 34 | ev.target.classList.add('ion-ios-resize'); 35 | } 36 | } else { 37 | originalOverviewHeight = document.getElementById('module-graph-svg').style.height; 38 | document.getElementById('module-graph-svg').style.height = '85vh'; 39 | overviewFullscreen = true; 40 | if (ev.target) { 41 | ev.target.classList.remove('ion-ios-resize'); 42 | ev.target.classList.add('ion-md-close'); 43 | } 44 | } 45 | document.getElementById('module-graph-svg').querySelector('svg').style.height = document.getElementById('module-graph-svg').clientHeight; 46 | setTimeout(function() { 47 | panZoom.resize(); 48 | panZoom.fit(); 49 | panZoom.center(); 50 | }, 0) 51 | }); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /docs/js/tabs.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var tabs = document.getElementsByClassName('nav-tabs'), 3 | updateAddress = function(e) { 4 | if(history.pushState && e.target.dataset.link) { 5 | history.pushState(null, null, '#' + e.target.dataset.link); 6 | } 7 | }; 8 | if (tabs.length > 0) { 9 | tabs = tabs[0].querySelectorAll('li'); 10 | for (var i = 0; i < tabs.length; i++) { 11 | tabs[i].addEventListener('click', updateAddress); 12 | var linkTag = tabs[i].querySelector('a'); 13 | if (location.hash !== '') { 14 | var currentHash = location.hash.substr(1); 15 | if (currentHash === linkTag.dataset.link) { 16 | linkTag.click(); 17 | } 18 | } 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /docs/js/tree.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | var tabs = document.getElementsByClassName('nav-tabs')[0], 3 | tabsCollection = tabs.getElementsByTagName('A'), 4 | treeTab; 5 | var len = tabsCollection.length; 6 | for(var i = 0; i < len; i++) { 7 | if (tabsCollection[i].getAttribute('id') === 'tree-tab') { 8 | treeTab = tabsCollection[i]; 9 | } 10 | } 11 | 12 | // short-circuit if no tree tab 13 | if (!treeTab) return; 14 | 15 | var handler = new Tautologistics.NodeHtmlParser.HtmlBuilder(function(error, dom) { 16 | if (error) { 17 | console.log('handler ko'); 18 | } 19 | }), 20 | parser = new Tautologistics.NodeHtmlParser.Parser(handler), 21 | currentLocation = window.location; 22 | parser.parseComplete(COMPONENT_TEMPLATE); 23 | 24 | var newNodes = [], 25 | newEdges = [], 26 | parsedHtml = handler.dom[0], 27 | nodeCount = 0, 28 | nodeLevel = 0; 29 | 30 | newNodes.push({ 31 | _id: 0, 32 | label: parsedHtml.name, 33 | type: parsedHtml.type 34 | }) 35 | //Add id for nodes 36 | var traverseIds = function(o) { 37 | for (i in o) { 38 | if (!!o[i] && typeof(o[i]) == "object") { 39 | if (!o[i].length && o[i].type === 'tag') { 40 | nodeCount += 1; 41 | o[i]._id = nodeCount; 42 | } 43 | traverseIds(o[i]); 44 | } 45 | } 46 | } 47 | parsedHtml._id = 0; 48 | traverseIds(parsedHtml); 49 | 50 | 51 | var DeepIterator = deepIterator.default, 52 | it = DeepIterator(parsedHtml); 53 | for (let { 54 | value, 55 | parent, 56 | parentNode, 57 | key, 58 | type 59 | } of it) { 60 | if (type === 'NonIterableObject' && typeof key !== 'undefined' && value.type === 'tag') { 61 | var newNode = { 62 | id: value._id, 63 | label: value.name, 64 | type: value.type 65 | }; 66 | for(var i = 0; i < COMPONENTS.length; i++) { 67 | if (COMPONENTS[i].selector === value.name) { 68 | newNode.font = { 69 | multi: 'html' 70 | }; 71 | newNode.label = '' + newNode.label + ''; 72 | newNode.color = '#FB7E81'; 73 | newNode.name = COMPONENTS[i].name; 74 | } 75 | } 76 | for(var i = 0; i < DIRECTIVES.length; i++) { 77 | if (value.attributes) { 78 | for(attr in value.attributes) { 79 | if (DIRECTIVES[i].selector.indexOf(attr) !== -1) { 80 | newNode.font = { 81 | multi: 'html' 82 | }; 83 | newNode.label = '' + newNode.label + ''; 84 | newNode.color = '#FF9800'; 85 | newNode.name = DIRECTIVES[i].name; 86 | } 87 | } 88 | } 89 | } 90 | newNodes.push(newNode); 91 | newEdges.push({ 92 | from: parentNode._parent._id, 93 | to: value._id, 94 | arrows: 'to' 95 | }); 96 | } 97 | } 98 | 99 | newNodes.shift(); 100 | 101 | var container = document.getElementById('tree-container'), 102 | data = { 103 | nodes: newNodes, 104 | edges: newEdges 105 | }, 106 | options = { 107 | layout: { 108 | hierarchical: { 109 | sortMethod: 'directed', 110 | enabled: true 111 | } 112 | }, 113 | nodes: { 114 | shape: 'ellipse', 115 | fixed: true 116 | } 117 | }, 118 | 119 | handleClickNode = function(params) { 120 | var clickeNodeId; 121 | if (params.nodes.length > 0) { 122 | clickeNodeId = params.nodes[0]; 123 | for(var i = 0; i < newNodes.length; i++) { 124 | if (newNodes[i].id === clickeNodeId) { 125 | for(var j = 0; j < COMPONENTS.length; j++) { 126 | if (COMPONENTS[j].name === newNodes[i].name) { 127 | document.location.href = currentLocation.origin + currentLocation.pathname.replace(ACTUAL_COMPONENT.name, newNodes[i].name); 128 | } 129 | } 130 | } 131 | } 132 | } 133 | }, 134 | 135 | loadTree = function () { 136 | setTimeout(function() { 137 | container.style.height = document.getElementsByClassName('content')[0].offsetHeight - 140 + 'px'; 138 | var network = new vis.Network(container, data, options); 139 | network.on('click', handleClickNode); 140 | }, 200); // Fade is 0.150 141 | }; 142 | 143 | loadTree(); 144 | treeTab.addEventListener('click', function() { 145 | loadTree(); 146 | }); 147 | }); 148 | -------------------------------------------------------------------------------- /docs/license.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 65 | 66 | 72 | 73 | 76 | 77 |
      78 |
      79 | 82 | 83 |
      84 |
      85 | 86 |

      MIT License

      87 |

      Copyright (c) 2021 Muhammad Ahsan Ayaz

      88 |

      Permission is hereby granted, free of charge, to any person obtaining a copy 89 | of this software and associated documentation files (the "Software"), to deal 90 | in the Software without restriction, including without limitation the rights 91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 92 | copies of the Software, and to permit persons to whom the Software is 93 | furnished to do so, subject to the following conditions:

      94 |

      The above copyright notice and this permission notice shall be included in all 95 | copies or substantial portions of the Software.

      96 |

      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 97 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 98 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 99 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 100 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 101 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 102 | SOFTWARE.

      103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 |
      125 |
      126 |

      results matching ""

      127 |
        128 |
        129 |
        130 |

        No results matching ""

        131 |
        132 |
        133 |
        134 | 135 |
        136 |
        137 | 138 | 146 | 147 | 153 | 154 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 65 | 66 | 72 | 73 | 76 | 77 |
        78 |
        79 | 82 | 83 |
        84 |
        85 | 86 | 87 | 88 | 91 |
        92 |
        93 |
        94 |
        95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
        114 |
        115 |

        results matching ""

        116 |
          117 |
          118 |
          119 |

          No results matching ""

          120 |
          121 |
          122 |
          123 | 124 |
          125 |
          126 | 127 | 135 | 136 | 142 | 143 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/modules/AppModule/dependencies.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | dependencies 11 | 12 | Legend 13 | 14 |  Declarations 15 | 16 |  Module 17 | 18 |  Bootstrap 19 | 20 |  Providers 21 | 22 |  Exports 23 | 24 | cluster_AppModule 25 | 26 | 27 | 28 | cluster_AppModule_declarations 29 | 30 | 31 | 32 | cluster_AppModule_bootstrap 33 | 34 | 35 | 36 | 37 | AppComponent 38 | 39 | AppComponent 40 | 41 | 42 | 43 | AppModule 44 | 45 | AppModule 46 | 47 | 48 | 49 | AppComponent->AppModule 50 | 51 | 52 | 53 | 54 | 55 | KeysPipe 56 | 57 | KeysPipe 58 | 59 | 60 | 61 | KeysPipe->AppModule 62 | 63 | 64 | 65 | 66 | 67 | AppComponent 68 | 69 | AppComponent 70 | 71 | 72 | 73 | AppModule->AppComponent 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /docs/modules/AppServerModule/dependencies.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | dependencies 11 | 12 | Legend 13 | 14 |  Declarations 15 | 16 |  Module 17 | 18 |  Bootstrap 19 | 20 |  Providers 21 | 22 |  Exports 23 | 24 | cluster_AppServerModule 25 | 26 | 27 | 28 | cluster_AppServerModule_imports 29 | 30 | 31 | 32 | cluster_AppServerModule_bootstrap 33 | 34 | 35 | 36 | 37 | AppModule 38 | 39 | AppModule 40 | 41 | 42 | 43 | AppServerModule 44 | 45 | AppServerModule 46 | 47 | 48 | 49 | AppModule->AppServerModule 50 | 51 | 52 | 53 | 54 | 55 | AppComponent 56 | 57 | AppComponent 58 | 59 | 60 | 61 | AppServerModule->AppComponent 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/modules/DeviceDetectorModule.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 24 | 25 |
          26 |
          27 | 30 | 31 |
          32 |
          33 | 34 | 35 | 36 | 40 | 41 | 42 | 50 | 51 |
          52 |
          53 | 54 |

          55 |

          File

          56 |

          57 |

          58 | src/index.ts 59 |

          60 | 61 | 62 | 63 | 64 |
          65 |
          66 |
          67 |
          68 | 69 |
          70 | 71 |

          72 | Methods 73 |

          74 | 75 | 76 | 77 | 87 | 88 | 89 | 93 | 94 | 95 | 96 | 97 | 101 | 102 | 103 | 104 | 105 | 112 | 113 | 114 |
          78 | 79 | 80 | 81 | Static 82 | forRoot 83 | 84 | 85 | 86 |
          90 | 91 | forRoot() 92 |
          98 |
          Defined in src/index.ts:9
          100 |
          106 | 107 |
          108 | Returns : ModuleWithProviders<DeviceDetectorModule> 109 | 110 |
          111 |
          115 |
          116 |
          117 | 118 | 119 |
          120 |
          import { NgModule, ModuleWithProviders } from '@angular/core'
          121 | import { CommonModule } from '@angular/common'
          122 | import { DeviceDetectorService } from './device-detector.service'
          123 | 
          124 | @NgModule({
          125 |   imports: [CommonModule],
          126 | })
          127 | export class DeviceDetectorModule {
          128 |   static forRoot(): ModuleWithProviders<DeviceDetectorModule> {
          129 |     return {
          130 |       ngModule: DeviceDetectorModule,
          131 |       providers: [DeviceDetectorService],
          132 |     }
          133 |   }
          134 | }
          135 | 
          136 | export { DeviceDetectorService, DeviceInfo } from './device-detector.service'
          137 | export { ReTree } from './retree'
          138 | export * from './device-detector.constants'
          139 | 
          140 |
          141 |
          142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
          158 |
          159 |

          result-matching ""

          160 |
            161 |
            162 |
            163 |

            No results matching ""

            164 |
            165 |
            166 |
            167 | 168 |
            169 |
            170 | 171 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /docs/overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 65 | 66 | 72 | 73 | 76 | 77 |
            78 |
            79 | 82 | 83 |
            84 |
            85 | 86 | 87 | 90 | 91 |
            92 |
            93 |
            94 |
            95 |
            96 |

            97 |

            1 Component

            98 |
            99 |
            100 |
            101 |
            102 |
            103 |
            104 |

            105 |

            1 Injectable

            106 |
            107 |
            108 |
            109 |
            110 |
            111 |
            112 |

            113 |

            1 Pipe

            114 |
            115 |
            116 |
            117 |
            118 |
            119 |
            120 |

            121 |

            2 Classes

            122 |
            123 |
            124 |
            125 |
            126 |
            127 |
            128 |

            129 |

            1 Interface

            130 |
            131 |
            132 |
            133 |
            134 |
            135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 |
            156 |
            157 |

            results matching ""

            158 |
              159 |
              160 |
              161 |

              No results matching ""

              162 |
              163 |
              164 |
              165 | 166 |
              167 |
              168 | 169 | 177 | 178 | 184 | 185 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /docs/properties.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ngx-device-detector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 65 | 66 | 72 | 73 | 76 | 77 |
              78 |
              79 | 82 | 83 |
              84 |
              85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 104 |
                105 |
              • 106 | Version : 10.1.0
              • 107 |
              • 108 | Keywords : angular, device, device detector, device detection, ngx-device-detector, angular device detector, angular5, angular6, angular7, angular8, angular18, angular19, angular20
              • 109 |
              • 110 | Bugs : https://github.com/AhsanAyaz/ngx-device-detector.git/issues
              • 111 |
              • 112 | License : MIT
              • 113 |
              • 114 | Repository : https://github.com/AhsanAyaz/ngx-device-detector.git
              • 115 |
              • 116 | Author : Muhammad Ahsan Ayaz
              • 117 |
              118 | 119 | 120 | 121 | 122 | 123 |
              124 |
              125 |

              results matching ""

              126 |
                127 |
                128 |
                129 |

                No results matching ""

                130 |
                131 |
                132 |
                133 | 134 |
                135 |
                136 | 137 | 145 | 146 | 152 | 153 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /docs/styles/bootstrap-card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | position: relative; 3 | display: block; 4 | margin-bottom: 20px; 5 | background-color: #fff; 6 | border: 1px solid #ddd; 7 | border-radius: 4px; 8 | } 9 | 10 | .card-block { 11 | padding: 15px; 12 | } 13 | .card-block:before, .card-block:after { 14 | content: " "; 15 | display: table; 16 | } 17 | .card-block:after { 18 | clear: both; 19 | } 20 | 21 | .card-title { 22 | margin: 5px; 23 | margin-bottom: 2px; 24 | text-align: center; 25 | } 26 | 27 | .card-subtitle { 28 | margin-top: -10px; 29 | margin-bottom: 0; 30 | } 31 | 32 | .card-text:last-child { 33 | margin-bottom: 0; 34 | margin-top: 10px; 35 | } 36 | 37 | .card-link:hover { 38 | text-decoration: none; 39 | } 40 | .card-link + .card-link { 41 | margin-left: 15px; 42 | } 43 | 44 | .card > .list-group:first-child .list-group-item:first-child { 45 | border-top-right-radius: 4px; 46 | border-top-left-radius: 4px; 47 | } 48 | .card > .list-group:last-child .list-group-item:last-child { 49 | border-bottom-right-radius: 4px; 50 | border-bottom-left-radius: 4px; 51 | } 52 | 53 | .card-header { 54 | padding: 10px 15px; 55 | background-color: #f5f5f5; 56 | border-bottom: 1px solid #ddd; 57 | } 58 | .card-header:before, .card-header:after { 59 | content: " "; 60 | display: table; 61 | } 62 | .card-header:after { 63 | clear: both; 64 | } 65 | .card-header:first-child { 66 | border-radius: 4px 4px 0 0; 67 | } 68 | 69 | .card-footer { 70 | padding: 10px 15px; 71 | background-color: #f5f5f5; 72 | border-top: 1px solid #ddd; 73 | } 74 | .card-footer:before, .card-footer:after { 75 | content: " "; 76 | display: table; 77 | } 78 | .card-footer:after { 79 | clear: both; 80 | } 81 | .card-footer:last-child { 82 | border-radius: 0 0 4px 4px; 83 | } 84 | 85 | .card-header-tabs { 86 | margin-right: -5px; 87 | margin-bottom: -10px; 88 | margin-left: -5px; 89 | border-bottom: 0; 90 | } 91 | 92 | .card-header-pills { 93 | margin-right: -5px; 94 | margin-left: -5px; 95 | } 96 | 97 | .card-primary { 98 | background-color: #337ab7; 99 | border-color: #337ab7; 100 | } 101 | .card-primary .card-header, 102 | .card-primary .card-footer { 103 | background-color: transparent; 104 | } 105 | 106 | .card-success { 107 | background-color: #5cb85c; 108 | border-color: #5cb85c; 109 | } 110 | .card-success .card-header, 111 | .card-success .card-footer { 112 | background-color: transparent; 113 | } 114 | 115 | .card-info { 116 | background-color: #5bc0de; 117 | border-color: #5bc0de; 118 | } 119 | .card-info .card-header, 120 | .card-info .card-footer { 121 | background-color: transparent; 122 | } 123 | 124 | .card-warning { 125 | background-color: #f0ad4e; 126 | border-color: #f0ad4e; 127 | } 128 | .card-warning .card-header, 129 | .card-warning .card-footer { 130 | background-color: transparent; 131 | } 132 | 133 | .card-danger { 134 | background-color: #d9534f; 135 | border-color: #d9534f; 136 | } 137 | .card-danger .card-header, 138 | .card-danger .card-footer { 139 | background-color: transparent; 140 | } 141 | 142 | .card-outline-primary { 143 | background-color: transparent; 144 | border-color: #337ab7; 145 | } 146 | 147 | .card-outline-secondary { 148 | background-color: transparent; 149 | border-color: #ccc; 150 | } 151 | 152 | .card-outline-info { 153 | background-color: transparent; 154 | border-color: #5bc0de; 155 | } 156 | 157 | .card-outline-success { 158 | background-color: transparent; 159 | border-color: #5cb85c; 160 | } 161 | 162 | .card-outline-warning { 163 | background-color: transparent; 164 | border-color: #f0ad4e; 165 | } 166 | 167 | .card-outline-danger { 168 | background-color: transparent; 169 | border-color: #d9534f; 170 | } 171 | 172 | .card-inverse .card-header, 173 | .card-inverse .card-footer { 174 | border-color: rgba(255, 255, 255, 0.2); 175 | } 176 | .card-inverse .card-header, 177 | .card-inverse .card-footer, 178 | .card-inverse .card-title, 179 | .card-inverse .card-blockquote { 180 | color: #fff; 181 | } 182 | .card-inverse .card-link, 183 | .card-inverse .card-text, 184 | .card-inverse .card-subtitle, 185 | .card-inverse .card-blockquote .blockquote-footer { 186 | color: rgba(255, 255, 255, 0.65); 187 | } 188 | .card-inverse .card-link:hover, .card-inverse .card-link:focus { 189 | color: #fff; 190 | } 191 | 192 | .card-blockquote { 193 | padding: 0; 194 | margin-bottom: 0; 195 | border-left: 0; 196 | } 197 | 198 | .card-img { 199 | border-radius: .25em; 200 | } 201 | 202 | .card-img-overlay { 203 | position: absolute; 204 | top: 0; 205 | right: 0; 206 | bottom: 0; 207 | left: 0; 208 | padding: 15px; 209 | } 210 | 211 | .card-img-top { 212 | border-top-right-radius: 4px; 213 | border-top-left-radius: 4px; 214 | } 215 | 216 | .card-img-bottom { 217 | border-bottom-right-radius: 4px; 218 | border-bottom-left-radius: 4px; 219 | } 220 | -------------------------------------------------------------------------------- /docs/styles/dark.css: -------------------------------------------------------------------------------- 1 | body.dark { 2 | background: #212121; 3 | color: #fafafa; 4 | } 5 | 6 | .dark code { 7 | color: #e09393; 8 | } 9 | 10 | .dark a, 11 | .dark .menu ul.list li a.active { 12 | color: #7fc9ff; 13 | } 14 | 15 | .dark .menu { 16 | background: #212121; 17 | border-right: 1px solid #444; 18 | } 19 | 20 | .dark .menu ul.list li a { 21 | color: #fafafa; 22 | } 23 | 24 | .dark .menu ul.list li.divider { 25 | background: #444; 26 | } 27 | 28 | .dark .xs-menu ul.list li:nth-child(2) { 29 | margin: 0; 30 | background: none; 31 | } 32 | 33 | .dark .menu ul.list li:nth-child(2) { 34 | margin: 0; 35 | background: none; 36 | } 37 | 38 | .dark #book-search-input { 39 | background: #212121; 40 | border-top: 1px solid #444; 41 | border-bottom: 1px solid #444; 42 | color: #fafafa; 43 | } 44 | 45 | .dark .table.metadata > tbody > tr:hover { 46 | color: #555; 47 | } 48 | 49 | .dark .table-bordered { 50 | border: 1px solid #444; 51 | } 52 | 53 | .dark .table-bordered > tbody > tr > td, 54 | .dark .table-bordered > tbody > tr > th, 55 | .dark .table-bordered > tfoot > tr > td, 56 | .dark .table-bordered > tfoot > tr > th, 57 | .dark .table-bordered > thead > tr > td, 58 | .dark .table-bordered > thead > tr > th { 59 | border: 1px solid #444; 60 | } 61 | 62 | .dark .coverage a, 63 | .dark .coverage-count { 64 | color: #fafafa; 65 | } 66 | 67 | .dark .coverage-header { 68 | color: black; 69 | } 70 | 71 | .dark .routes svg text, 72 | .dark .routes svg a { 73 | fill: white; 74 | } 75 | .dark .routes svg rect { 76 | fill: #212121 !important; 77 | } 78 | 79 | .dark .navbar-default, 80 | .dark .btn-default { 81 | background-color: black; 82 | border-color: #444; 83 | color: #fafafa; 84 | } 85 | 86 | .dark .navbar-default .navbar-brand { 87 | color: #fafafa; 88 | } 89 | 90 | .dark .overview .card, 91 | .dark .modules .card { 92 | background: #171717; 93 | color: #fafafa; 94 | border: 1px solid #444; 95 | } 96 | .dark .overview .card a { 97 | color: #fafafa; 98 | } 99 | 100 | .dark .modules .card-header { 101 | background: none; 102 | border-bottom: 1px solid #444; 103 | } 104 | 105 | .dark .module .list-group-item { 106 | background: none; 107 | border: 1px solid #444; 108 | } 109 | 110 | .dark .container-fluid.module h3 a { 111 | color: #337ab7; 112 | } 113 | 114 | .dark table.params thead { 115 | background: #484848; 116 | color: #fafafa; 117 | } 118 | 119 | .dark .content table { 120 | --bs-table-color: #fafafa; 121 | } 122 | -------------------------------------------------------------------------------- /docs/styles/laravel.css: -------------------------------------------------------------------------------- 1 | .nav-tabs > li > a { 2 | text-decoration: none; 3 | } 4 | 5 | .navbar-default .navbar-brand { 6 | color: #f4645f; 7 | text-decoration: none; 8 | font-size: 16px; 9 | } 10 | 11 | .menu ul.list li a[data-type='chapter-link'], 12 | .menu ul.list li.chapter .simple { 13 | color: #525252; 14 | border-bottom: 1px dashed rgba(0, 0, 0, 0.1); 15 | } 16 | 17 | .content h1, 18 | .content h2, 19 | .content h3, 20 | .content h4, 21 | .content h5 { 22 | color: #292e31; 23 | font-weight: normal; 24 | } 25 | 26 | .content { 27 | color: #4c555a; 28 | } 29 | 30 | a { 31 | color: #f4645f; 32 | text-decoration: underline; 33 | } 34 | a:hover { 35 | color: #f1362f; 36 | } 37 | 38 | .menu ul.list li:nth-child(2) { 39 | margin-top: 0; 40 | } 41 | 42 | .menu ul.list li.title a { 43 | color: #f4645f; 44 | text-decoration: none; 45 | font-size: 16px; 46 | } 47 | 48 | .menu ul.list li a { 49 | color: #f4645f; 50 | text-decoration: none; 51 | } 52 | .menu ul.list li a.active { 53 | color: #f4645f; 54 | font-weight: bold; 55 | } 56 | 57 | code { 58 | box-sizing: border-box; 59 | display: inline-block; 60 | padding: 0 5px; 61 | background: #f0f2f1; 62 | border-radius: 3px; 63 | color: #b93d6a; 64 | font-size: 13px; 65 | line-height: 20px; 66 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125); 67 | } 68 | 69 | pre { 70 | margin: 0; 71 | padding: 12px 12px; 72 | background: rgba(238, 238, 238, 0.35); 73 | border-radius: 3px; 74 | font-size: 13px; 75 | line-height: 1.5em; 76 | font-weight: 500; 77 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125); 78 | } 79 | 80 | .dark body { 81 | color: #fafafa; 82 | } 83 | .dark .content h1, 84 | .dark .content h2, 85 | .dark .content h3, 86 | .dark .content h4, 87 | .dark .content h5 { 88 | color: #fafafa; 89 | } 90 | 91 | .dark code { 92 | background: none; 93 | } 94 | 95 | .dark .content { 96 | color: #fafafa; 97 | } 98 | 99 | .dark .menu ul.list li a[data-type='chapter-link'], 100 | .dark .menu ul.list li.chapter .simple { 101 | color: #fafafa; 102 | } 103 | 104 | .dark .menu ul.list li.title a { 105 | color: #fafafa; 106 | } 107 | 108 | .dark .menu ul.list li a { 109 | color: #fafafa; 110 | } 111 | .dark .menu ul.list li a.active { 112 | color: #7fc9ff; 113 | } 114 | -------------------------------------------------------------------------------- /docs/styles/material.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | background: none; 3 | } 4 | 5 | a:hover { 6 | text-decoration: none; 7 | } 8 | 9 | /** LINK **/ 10 | 11 | .menu ul.list li a { 12 | text-decoration: none; 13 | } 14 | 15 | .menu ul.list li a:hover, 16 | .menu ul.list li.chapter .simple:hover { 17 | background-color: #f8f9fa; 18 | text-decoration: none; 19 | } 20 | 21 | #book-search-input { 22 | margin-bottom: 0; 23 | } 24 | 25 | .menu ul.list li.divider { 26 | margin-top: 0; 27 | background: #e9ecef; 28 | } 29 | 30 | .menu .title:hover { 31 | background-color: #f8f9fa; 32 | } 33 | 34 | /** CARD **/ 35 | 36 | .card { 37 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 38 | 0 1px 5px 0 rgba(0, 0, 0, 0.12); 39 | border-radius: 0.125rem; 40 | border: 0; 41 | margin-top: 1px; 42 | } 43 | 44 | .card-header { 45 | background: none; 46 | } 47 | 48 | /** BUTTON **/ 49 | 50 | .btn { 51 | border-radius: 0.125rem; 52 | } 53 | 54 | /** NAV BAR **/ 55 | 56 | .nav { 57 | border: 0; 58 | } 59 | .nav-tabs > li > a { 60 | border: 0; 61 | border-bottom: 0.214rem solid transparent; 62 | color: rgba(0, 0, 0, 0.54); 63 | margin-right: 0; 64 | } 65 | .nav-tabs > li.active > a, 66 | .nav-tabs > li.active > a:focus, 67 | .nav-tabs > li.active > a:hover { 68 | color: rgba(0, 0, 0, 0.87); 69 | border-top: 0; 70 | border-left: 0; 71 | border-right: 0; 72 | border-bottom: 0.214rem solid transparent; 73 | border-color: #008cff; 74 | font-weight: bold; 75 | } 76 | .nav > li > a:focus, 77 | .nav > li > a:hover { 78 | background: none; 79 | } 80 | 81 | /** LIST **/ 82 | 83 | .list-group-item:first-child { 84 | border-top-left-radius: 0.125rem; 85 | border-top-right-radius: 0.125rem; 86 | } 87 | .list-group-item:last-child { 88 | border-bottom-left-radius: 0.125rem; 89 | border-bottom-right-radius: 0.125rem; 90 | } 91 | 92 | /** MISC **/ 93 | 94 | .modifier { 95 | border-radius: 0.125rem; 96 | } 97 | 98 | pre[class*='language-'] { 99 | border-radius: 0.125rem; 100 | } 101 | 102 | /** TABLE **/ 103 | 104 | .table-hover > tbody > tr:hover { 105 | background: rgba(0, 0, 0, 0.075); 106 | } 107 | 108 | table.params thead { 109 | background: none; 110 | } 111 | table.params thead td { 112 | color: rgba(0, 0, 0, 0.54); 113 | font-weight: bold; 114 | } 115 | 116 | .dark .menu .title:hover { 117 | background-color: #2d2d2d; 118 | } 119 | .dark .menu ul.list li a:hover, 120 | .dark .menu ul.list li.chapter .simple:hover { 121 | background-color: #2d2d2d; 122 | } 123 | .dark .nav-tabs > li:not(.active) > a { 124 | color: #fafafa; 125 | } 126 | .dark table.params thead { 127 | background: #484848; 128 | } 129 | .dark table.params thead td { 130 | color: #fafafa; 131 | } 132 | -------------------------------------------------------------------------------- /docs/styles/original.css: -------------------------------------------------------------------------------- 1 | .navbar-default .navbar-brand, 2 | .menu ul.list li.title { 3 | font-weight: bold; 4 | color: #3c3c3c; 5 | padding-bottom: 5px; 6 | } 7 | 8 | .menu ul.list li a[data-type='chapter-link'], 9 | .menu ul.list li.chapter .simple { 10 | font-weight: bold; 11 | font-size: 14px; 12 | } 13 | 14 | .menu ul.list li a[href='./routes.html'] { 15 | border-bottom: none; 16 | } 17 | 18 | .menu ul.list > li:nth-child(2) { 19 | display: none; 20 | } 21 | 22 | .menu ul.list li.chapter ul.links { 23 | background: #fff; 24 | padding-left: 0; 25 | } 26 | 27 | .menu ul.list li.chapter ul.links li { 28 | border-bottom: 1px solid #ddd; 29 | padding-left: 20px; 30 | } 31 | 32 | .menu ul.list li.chapter ul.links li:last-child { 33 | border-bottom: none; 34 | } 35 | 36 | .menu ul.list li a.active { 37 | color: #337ab7; 38 | font-weight: bold; 39 | } 40 | 41 | #book-search-input { 42 | margin-bottom: 0; 43 | border-bottom: none; 44 | } 45 | .menu ul.list li.divider { 46 | margin: 0; 47 | } 48 | 49 | .dark .menu ul.list li.chapter ul.links { 50 | background: none; 51 | } 52 | -------------------------------------------------------------------------------- /docs/styles/postmark.css: -------------------------------------------------------------------------------- 1 | .navbar-default { 2 | background: #ffde00; 3 | border: none; 4 | } 5 | 6 | .navbar-default .navbar-brand { 7 | color: #333; 8 | font-weight: bold; 9 | } 10 | 11 | .menu { 12 | background: #333; 13 | color: #fcfcfc; 14 | } 15 | 16 | .menu ul.list li a { 17 | color: #333; 18 | } 19 | 20 | .menu ul.list li.title { 21 | background: #ffde00; 22 | color: #333; 23 | padding-bottom: 5px; 24 | } 25 | 26 | .menu ul.list li:nth-child(2) { 27 | margin-top: 0; 28 | } 29 | 30 | .menu ul.list li.chapter a, 31 | .menu ul.list li.chapter .simple { 32 | color: white; 33 | text-decoration: none; 34 | } 35 | 36 | .menu ul.list li.chapter ul.links a { 37 | color: #949494; 38 | text-transform: none; 39 | padding-left: 35px; 40 | } 41 | 42 | .menu ul.list li.chapter ul.links a:hover, 43 | .menu ul.list li.chapter ul.links a.active { 44 | color: #ffde00; 45 | } 46 | 47 | .menu ul.list li.chapter ul.links { 48 | padding-left: 0; 49 | } 50 | 51 | .menu ul.list li.divider { 52 | background: rgba(255, 255, 255, 0.07); 53 | } 54 | 55 | #book-search-input input, 56 | #book-search-input input:focus, 57 | #book-search-input input:hover { 58 | color: #949494; 59 | } 60 | 61 | .copyright { 62 | color: #b3b3b3; 63 | background: #272525; 64 | } 65 | 66 | .content { 67 | background: #fcfcfc; 68 | } 69 | 70 | .content a { 71 | color: #007dcc; 72 | } 73 | 74 | .content a:visited { 75 | color: #0165a5; 76 | } 77 | 78 | .menu ul.list li:nth-last-child(2) { 79 | background: none; 80 | } 81 | 82 | .list-group-item:first-child, 83 | .list-group-item:last-child { 84 | border-radius: 0; 85 | } 86 | 87 | .menu ul.list li.title a { 88 | text-decoration: none; 89 | font-weight: bold; 90 | } 91 | 92 | .menu ul.list li.title a:hover { 93 | background: rgba(255, 255, 255, 0.1); 94 | } 95 | 96 | .breadcrumb > li + li:before { 97 | content: '»\00a0'; 98 | } 99 | 100 | .breadcrumb { 101 | padding-bottom: 15px; 102 | border-bottom: 1px solid #e1e4e5; 103 | } 104 | 105 | code { 106 | white-space: nowrap; 107 | max-width: 100%; 108 | background: #f5f5f5; 109 | padding: 2px 5px; 110 | color: #666666; 111 | overflow-x: auto; 112 | border-radius: 0; 113 | } 114 | 115 | pre { 116 | white-space: pre; 117 | margin: 0; 118 | padding: 12px 12px; 119 | font-size: 12px; 120 | line-height: 1.5; 121 | display: block; 122 | overflow: auto; 123 | color: #404040; 124 | background: #f3f3f3; 125 | } 126 | 127 | pre code.hljs { 128 | border: none; 129 | background: inherit; 130 | } 131 | 132 | /* 133 | Atom One Light by Daniel Gamage 134 | Original One Light Syntax theme from https://github.com/atom/one-light-syntax 135 | base: #fafafa 136 | mono-1: #383a42 137 | mono-2: #686b77 138 | mono-3: #a0a1a7 139 | hue-1: #0184bb 140 | hue-2: #4078f2 141 | hue-3: #a626a4 142 | hue-4: #50a14f 143 | hue-5: #e45649 144 | hue-5-2: #c91243 145 | hue-6: #986801 146 | hue-6-2: #c18401 147 | */ 148 | 149 | .hljs { 150 | display: block; 151 | overflow-x: auto; 152 | padding: 0.5em; 153 | color: #383a42; 154 | background: #fafafa; 155 | } 156 | 157 | .hljs-comment, 158 | .hljs-quote { 159 | color: #a0a1a7; 160 | font-style: italic; 161 | } 162 | 163 | .hljs-doctag, 164 | .hljs-keyword, 165 | .hljs-formula { 166 | color: #a626a4; 167 | } 168 | 169 | .hljs-section, 170 | .hljs-name, 171 | .hljs-selector-tag, 172 | .hljs-deletion, 173 | .hljs-subst { 174 | color: #e45649; 175 | } 176 | 177 | .hljs-literal { 178 | color: #0184bb; 179 | } 180 | 181 | .hljs-string, 182 | .hljs-regexp, 183 | .hljs-addition, 184 | .hljs-attribute, 185 | .hljs-meta-string { 186 | color: #50a14f; 187 | } 188 | 189 | .hljs-built_in, 190 | .hljs-class .hljs-title { 191 | color: #c18401; 192 | } 193 | 194 | .hljs-attr, 195 | .hljs-variable, 196 | .hljs-template-variable, 197 | .hljs-type, 198 | .hljs-selector-class, 199 | .hljs-selector-attr, 200 | .hljs-selector-pseudo, 201 | .hljs-number { 202 | color: #986801; 203 | } 204 | 205 | .hljs-symbol, 206 | .hljs-bullet, 207 | .hljs-link, 208 | .hljs-meta, 209 | .hljs-selector-id, 210 | .hljs-title { 211 | color: #4078f2; 212 | } 213 | 214 | .hljs-emphasis { 215 | font-style: italic; 216 | } 217 | 218 | .hljs-strong { 219 | font-weight: bold; 220 | } 221 | 222 | .hljs-link { 223 | text-decoration: underline; 224 | } 225 | 226 | .dark .content { 227 | background: none; 228 | } 229 | .dark code { 230 | background: none; 231 | color: #e09393; 232 | } 233 | .dark .menu ul.list li.chapter a.active { 234 | color: #ffde00; 235 | } 236 | .dark .menu { 237 | background: #272525; 238 | } 239 | -------------------------------------------------------------------------------- /docs/styles/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.24.0 2 | https://prismjs.com/download.html?#themes=prism-okaidia&languages=markup+css+clike+javascript+apacheconf+aspnet+bash+c+csharp+cpp+coffeescript+dart+docker+elm+git+go+graphql+handlebars+haskell+http+ignore+java+json+kotlin+less+markdown+markup-templating+nginx+php+powershell+ruby+rust+sass+scss+sql+swift+typescript+wasm+yaml&plugins=line-highlight+line-numbers+toolbar+copy-to-clipboard */ 3 | /** 4 | * okaidia theme for JavaScript, CSS and HTML 5 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/ 6 | * @author ocodia 7 | */ 8 | 9 | code[class*='language-'], 10 | pre[class*='language-'] { 11 | color: #f8f8f2; 12 | background: none; 13 | text-shadow: 0 1px rgba(0, 0, 0, 0.3); 14 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 15 | font-size: 1em; 16 | text-align: left; 17 | white-space: pre; 18 | word-spacing: normal; 19 | word-break: normal; 20 | word-wrap: normal; 21 | line-height: 1.5; 22 | 23 | -moz-tab-size: 4; 24 | -o-tab-size: 4; 25 | tab-size: 4; 26 | 27 | -webkit-hyphens: none; 28 | -moz-hyphens: none; 29 | -ms-hyphens: none; 30 | hyphens: none; 31 | } 32 | 33 | /* Code blocks */ 34 | pre[class*='language-'] { 35 | padding: 1em; 36 | margin: 0.5em 0; 37 | overflow: auto; 38 | border-radius: 0.3em; 39 | } 40 | 41 | :not(pre) > code[class*='language-'], 42 | pre[class*='language-'] { 43 | background: #272822; 44 | } 45 | 46 | /* Inline code */ 47 | :not(pre) > code[class*='language-'] { 48 | padding: 0.1em; 49 | border-radius: 0.3em; 50 | white-space: normal; 51 | } 52 | 53 | .token.comment, 54 | .token.prolog, 55 | .token.doctype, 56 | .token.cdata { 57 | color: #8292a2; 58 | } 59 | 60 | .token.punctuation { 61 | color: #f8f8f2; 62 | } 63 | 64 | .token.namespace { 65 | opacity: 0.7; 66 | } 67 | 68 | .token.property, 69 | .token.tag, 70 | .token.constant, 71 | .token.symbol, 72 | .token.deleted { 73 | color: #f92672; 74 | } 75 | 76 | .token.boolean, 77 | .token.number { 78 | color: #ae81ff; 79 | } 80 | 81 | .token.selector, 82 | .token.attr-name, 83 | .token.string, 84 | .token.char, 85 | .token.builtin, 86 | .token.inserted { 87 | color: #a6e22e; 88 | } 89 | 90 | .token.operator, 91 | .token.entity, 92 | .token.url, 93 | .language-css .token.string, 94 | .style .token.string, 95 | .token.variable { 96 | color: #f8f8f2; 97 | } 98 | 99 | .token.atrule, 100 | .token.attr-value, 101 | .token.function, 102 | .token.class-name { 103 | color: #e6db74; 104 | } 105 | 106 | .token.keyword { 107 | color: #66d9ef; 108 | } 109 | 110 | .token.regex, 111 | .token.important { 112 | color: #fd971f; 113 | } 114 | 115 | .token.important, 116 | .token.bold { 117 | font-weight: bold; 118 | } 119 | .token.italic { 120 | font-style: italic; 121 | } 122 | 123 | .token.entity { 124 | cursor: help; 125 | } 126 | 127 | pre[data-line] { 128 | position: relative; 129 | padding: 1em 0 1em 3em; 130 | } 131 | 132 | .line-highlight { 133 | position: absolute; 134 | left: 0; 135 | right: 0; 136 | padding: inherit 0; 137 | margin-top: 1em; /* Same as .prism’s padding-top */ 138 | 139 | background: hsla(24, 20%, 50%, 0.08); 140 | background: linear-gradient(to right, hsla(24, 20%, 50%, 0.1) 70%, hsla(24, 20%, 50%, 0)); 141 | 142 | pointer-events: none; 143 | 144 | line-height: inherit; 145 | white-space: pre; 146 | } 147 | 148 | @media print { 149 | .line-highlight { 150 | /* 151 | * This will prevent browsers from replacing the background color with white. 152 | * It's necessary because the element is layered on top of the displayed code. 153 | */ 154 | -webkit-print-color-adjust: exact; 155 | color-adjust: exact; 156 | } 157 | } 158 | 159 | .line-highlight:before, 160 | .line-highlight[data-end]:after { 161 | content: attr(data-start); 162 | position: absolute; 163 | top: 0.4em; 164 | left: 0.6em; 165 | min-width: 1em; 166 | padding: 0 0.5em; 167 | background-color: hsla(24, 20%, 50%, 0.4); 168 | color: hsl(24, 20%, 95%); 169 | font: bold 65%/1.5 sans-serif; 170 | text-align: center; 171 | vertical-align: 0.3em; 172 | border-radius: 999px; 173 | text-shadow: none; 174 | box-shadow: 0 1px white; 175 | } 176 | 177 | .line-highlight[data-end]:after { 178 | content: attr(data-end); 179 | top: auto; 180 | bottom: 0.4em; 181 | } 182 | 183 | .line-numbers .line-highlight:before, 184 | .line-numbers .line-highlight:after { 185 | content: none; 186 | } 187 | 188 | pre[id].linkable-line-numbers span.line-numbers-rows { 189 | pointer-events: all; 190 | } 191 | pre[id].linkable-line-numbers span.line-numbers-rows > span:before { 192 | cursor: pointer; 193 | } 194 | pre[id].linkable-line-numbers span.line-numbers-rows > span:hover:before { 195 | background-color: rgba(128, 128, 128, 0.2); 196 | } 197 | 198 | pre[class*='language-'].line-numbers { 199 | position: relative; 200 | padding-left: 3.8em; 201 | counter-reset: linenumber; 202 | } 203 | 204 | pre[class*='language-'].line-numbers > code { 205 | position: relative; 206 | white-space: inherit; 207 | } 208 | 209 | .line-numbers .line-numbers-rows { 210 | position: absolute; 211 | pointer-events: none; 212 | top: 0; 213 | font-size: 100%; 214 | left: -3.8em; 215 | width: 3em; /* works for line-numbers below 1000 lines */ 216 | letter-spacing: -1px; 217 | border-right: 1px solid #999; 218 | 219 | -webkit-user-select: none; 220 | -moz-user-select: none; 221 | -ms-user-select: none; 222 | user-select: none; 223 | } 224 | 225 | .line-numbers-rows > span { 226 | display: block; 227 | counter-increment: linenumber; 228 | } 229 | 230 | .line-numbers-rows > span:before { 231 | content: counter(linenumber); 232 | color: #999; 233 | display: block; 234 | padding-right: 0.8em; 235 | text-align: right; 236 | } 237 | 238 | div.code-toolbar { 239 | position: relative; 240 | } 241 | 242 | div.code-toolbar > .toolbar { 243 | position: absolute; 244 | top: 0.3em; 245 | right: 0.2em; 246 | transition: opacity 0.3s ease-in-out; 247 | opacity: 0; 248 | } 249 | 250 | div.code-toolbar:hover > .toolbar { 251 | opacity: 1; 252 | } 253 | 254 | /* Separate line b/c rules are thrown out if selector is invalid. 255 | IE11 and old Edge versions don't support :focus-within. */ 256 | div.code-toolbar:focus-within > .toolbar { 257 | opacity: 1; 258 | } 259 | 260 | div.code-toolbar > .toolbar .toolbar-item { 261 | display: inline-block; 262 | } 263 | 264 | div.code-toolbar > .toolbar a { 265 | cursor: pointer; 266 | } 267 | 268 | div.code-toolbar > .toolbar button { 269 | background: none; 270 | border: 0; 271 | color: inherit; 272 | font: inherit; 273 | line-height: normal; 274 | overflow: visible; 275 | padding: 0; 276 | -webkit-user-select: none; /* for button */ 277 | -moz-user-select: none; 278 | -ms-user-select: none; 279 | } 280 | 281 | div.code-toolbar > .toolbar a, 282 | div.code-toolbar > .toolbar button, 283 | div.code-toolbar > .toolbar span { 284 | color: #bbb; 285 | font-size: 0.8em; 286 | padding: 0 0.5em; 287 | background: #f5f2f0; 288 | background: rgba(224, 224, 224, 0.2); 289 | box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); 290 | border-radius: 0.5em; 291 | } 292 | 293 | div.code-toolbar > .toolbar a:hover, 294 | div.code-toolbar > .toolbar a:focus, 295 | div.code-toolbar > .toolbar button:hover, 296 | div.code-toolbar > .toolbar button:focus, 297 | div.code-toolbar > .toolbar span:hover, 298 | div.code-toolbar > .toolbar span:focus { 299 | color: inherit; 300 | text-decoration: none; 301 | } 302 | -------------------------------------------------------------------------------- /docs/styles/readthedocs.css: -------------------------------------------------------------------------------- 1 | .navbar-default { 2 | background: #2980b9; 3 | border: none; 4 | } 5 | 6 | .navbar-default .navbar-brand { 7 | color: #fcfcfc; 8 | } 9 | 10 | .menu { 11 | background: #343131; 12 | color: #fcfcfc; 13 | } 14 | 15 | .menu ul.list li a { 16 | color: #fcfcfc; 17 | } 18 | 19 | .menu ul.list li.title { 20 | background: #2980b9; 21 | padding-bottom: 5px; 22 | } 23 | 24 | .menu ul.list li:nth-child(2) { 25 | margin-top: 0; 26 | } 27 | 28 | .menu ul.list li.chapter a, 29 | .menu ul.list li.chapter .simple { 30 | color: #555; 31 | text-transform: uppercase; 32 | text-decoration: none; 33 | } 34 | 35 | .menu ul.list li.chapter ul.links a { 36 | color: #b3b3b3; 37 | text-transform: none; 38 | padding-left: 35px; 39 | } 40 | 41 | .menu ul.list li.chapter ul.links a:hover { 42 | background: #4e4a4a; 43 | } 44 | 45 | .menu ul.list li.chapter a.active, 46 | .menu ul.list li.chapter ul.links a.active { 47 | color: #0099e5; 48 | } 49 | 50 | .menu ul.list li.chapter ul.links { 51 | padding-left: 0; 52 | } 53 | 54 | .menu ul.list li.divider { 55 | background: rgba(255, 255, 255, 0.07); 56 | } 57 | 58 | #book-search-input input, 59 | #book-search-input input:focus, 60 | #book-search-input input:hover { 61 | color: #949494; 62 | } 63 | 64 | .copyright { 65 | color: #b3b3b3; 66 | background: #272525; 67 | } 68 | 69 | .content { 70 | background: #fcfcfc; 71 | } 72 | 73 | .content a { 74 | color: #2980b9; 75 | } 76 | 77 | .content a:hover { 78 | color: #3091d1; 79 | } 80 | 81 | .content a:visited { 82 | color: #9b59b6; 83 | } 84 | 85 | .menu ul.list li:nth-last-child(2) { 86 | background: none; 87 | } 88 | 89 | code { 90 | white-space: nowrap; 91 | max-width: 100%; 92 | background: #fff; 93 | padding: 2px 5px; 94 | color: #e74c3c; 95 | overflow-x: auto; 96 | border-radius: 0; 97 | } 98 | 99 | pre { 100 | white-space: pre; 101 | margin: 0; 102 | padding: 12px 12px; 103 | font-size: 12px; 104 | line-height: 1.5; 105 | display: block; 106 | overflow: auto; 107 | color: #404040; 108 | background: rgba(238, 238, 238, 0.35); 109 | } 110 | 111 | .dark .content { 112 | background: none; 113 | } 114 | .dark code { 115 | background: none; 116 | color: #e09393; 117 | } 118 | -------------------------------------------------------------------------------- /docs/styles/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, 7 | body, 8 | div, 9 | span, 10 | applet, 11 | object, 12 | iframe, 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | blockquote, 21 | pre, 22 | a, 23 | abbr, 24 | acronym, 25 | address, 26 | big, 27 | cite, 28 | code, 29 | del, 30 | dfn, 31 | em, 32 | img, 33 | ins, 34 | kbd, 35 | q, 36 | s, 37 | samp, 38 | small, 39 | strike, 40 | strong, 41 | sub, 42 | sup, 43 | tt, 44 | var, 45 | b, 46 | u, 47 | i, 48 | center, 49 | dl, 50 | dt, 51 | dd, 52 | ol, 53 | ul, 54 | li, 55 | fieldset, 56 | form, 57 | label, 58 | legend, 59 | table, 60 | caption, 61 | tbody, 62 | tfoot, 63 | thead, 64 | tr, 65 | th, 66 | td, 67 | article, 68 | aside, 69 | canvas, 70 | details, 71 | embed, 72 | figure, 73 | figcaption, 74 | footer, 75 | header, 76 | hgroup, 77 | menu, 78 | nav, 79 | output, 80 | ruby, 81 | section, 82 | summary, 83 | time, 84 | mark, 85 | audio, 86 | video { 87 | margin: 0; 88 | padding: 0; 89 | border: 0; 90 | font: inherit; 91 | font-size: 100%; 92 | vertical-align: baseline; 93 | } 94 | /* HTML5 display-role reset for older browsers */ 95 | article, 96 | aside, 97 | details, 98 | figcaption, 99 | figure, 100 | footer, 101 | header, 102 | hgroup, 103 | menu, 104 | nav, 105 | section { 106 | display: block; 107 | } 108 | body { 109 | line-height: 1; 110 | } 111 | ol, 112 | ul { 113 | list-style: none; 114 | } 115 | blockquote, 116 | q { 117 | quotes: none; 118 | } 119 | blockquote:before, 120 | blockquote:after, 121 | q:before, 122 | q:after { 123 | content: ''; 124 | content: none; 125 | } 126 | table { 127 | border-collapse: collapse; 128 | border-spacing: 0; 129 | } 130 | -------------------------------------------------------------------------------- /docs/styles/stripe.css: -------------------------------------------------------------------------------- 1 | .navbar-default .navbar-brand { 2 | color: #0099e5; 3 | } 4 | 5 | .menu ul.list li a[data-type='chapter-link'], 6 | .menu ul.list li.chapter .simple { 7 | color: #939da3; 8 | text-transform: uppercase; 9 | } 10 | 11 | .content h1, 12 | .content h2, 13 | .content h3, 14 | .content h4, 15 | .content h5 { 16 | color: #292e31; 17 | font-weight: normal; 18 | } 19 | 20 | .content { 21 | color: #4c555a; 22 | } 23 | 24 | .menu ul.list li.title { 25 | padding: 5px 0; 26 | } 27 | 28 | a { 29 | color: #0099e5; 30 | text-decoration: none; 31 | } 32 | a:hover { 33 | color: #292e31; 34 | text-decoration: none; 35 | } 36 | 37 | .menu ul.list li:nth-child(2) { 38 | margin-top: 0; 39 | } 40 | 41 | .menu ul.list li.title a, 42 | .navbar a { 43 | color: #0099e5; 44 | text-decoration: none; 45 | font-size: 16px; 46 | } 47 | 48 | .menu ul.list li a.active { 49 | color: #0099e5; 50 | } 51 | 52 | code { 53 | box-sizing: border-box; 54 | display: inline-block; 55 | padding: 0 5px; 56 | background: #fafcfc; 57 | border-radius: 4px; 58 | color: #b93d6a; 59 | font-size: 13px; 60 | line-height: 20px; 61 | } 62 | 63 | pre { 64 | margin: 0; 65 | padding: 12px 12px; 66 | background: #272b2d; 67 | border-radius: 5px; 68 | font-size: 13px; 69 | line-height: 1.5em; 70 | font-weight: 500; 71 | } 72 | 73 | .dark body { 74 | color: #fafafa; 75 | } 76 | .dark .content h1, 77 | .dark .content h2, 78 | .dark .content h3, 79 | .dark .content h4, 80 | .dark .content h5 { 81 | color: #fafafa; 82 | } 83 | 84 | .dark code { 85 | background: none; 86 | } 87 | 88 | .dark .content { 89 | color: #fafafa; 90 | } 91 | 92 | .dark .menu ul.list li a[data-type='chapter-link'], 93 | .dark .menu ul.list li.chapter .simple { 94 | color: #fafafa; 95 | } 96 | 97 | .dark .menu ul.list li.title a { 98 | color: #fafafa; 99 | } 100 | 101 | .dark .menu ul.list li a { 102 | color: #fafafa; 103 | } 104 | .dark .menu ul.list li a.active { 105 | color: #7fc9ff; 106 | } 107 | -------------------------------------------------------------------------------- /docs/styles/style.css: -------------------------------------------------------------------------------- 1 | @import "./reset.css"; 2 | @import "./bootstrap.min.css"; 3 | @import "./bootstrap-card.css"; 4 | @import "./prism.css"; 5 | @import "./ionicons.min.css"; 6 | @import "./compodoc.css"; 7 | @import "./tablesort.css"; 8 | -------------------------------------------------------------------------------- /docs/styles/tablesort.css: -------------------------------------------------------------------------------- 1 | th[role=columnheader]:not(.no-sort) { 2 | cursor: pointer; 3 | } 4 | 5 | th[role=columnheader]:not(.no-sort):after { 6 | content: ''; 7 | float: right; 8 | margin-top: 7px; 9 | border-width: 0 4px 4px; 10 | border-style: solid; 11 | border-color: #404040 transparent; 12 | visibility: visible; 13 | opacity: 1; 14 | -ms-user-select: none; 15 | -webkit-user-select: none; 16 | -moz-user-select: none; 17 | user-select: none; 18 | } 19 | 20 | th[aria-sort=ascending]:not(.no-sort):after { 21 | border-bottom: none; 22 | border-width: 4px 4px 0; 23 | } 24 | 25 | th[aria-sort]:not(.no-sort):after { 26 | visibility: visible; 27 | opacity: 0.4; 28 | } 29 | 30 | th[role=columnheader]:not(.no-sort):hover:after { 31 | visibility: visible; 32 | opacity: 1; 33 | } 34 | -------------------------------------------------------------------------------- /docs/styles/vagrant.css: -------------------------------------------------------------------------------- 1 | .navbar-default .navbar-brand { 2 | background: white; 3 | color: #8d9ba8; 4 | } 5 | 6 | .menu .list { 7 | background: #0c5593; 8 | } 9 | 10 | .menu .chapter { 11 | padding: 0 20px; 12 | } 13 | 14 | .menu ul.list li a[data-type='chapter-link'], 15 | .menu ul.list li.chapter .simple { 16 | color: white; 17 | text-transform: uppercase; 18 | border-bottom: 1px solid rgba(255, 255, 255, 0.4); 19 | } 20 | 21 | .content h1, 22 | .content h2, 23 | .content h3, 24 | .content h4, 25 | .content h5 { 26 | color: #292e31; 27 | font-weight: normal; 28 | } 29 | 30 | .content { 31 | color: #4c555a; 32 | } 33 | 34 | a { 35 | color: #0094bf; 36 | text-decoration: underline; 37 | } 38 | a:hover { 39 | color: #f1362f; 40 | } 41 | 42 | .menu ul.list li.title { 43 | background: white; 44 | padding-bottom: 5px; 45 | } 46 | 47 | .menu ul.list li:nth-child(2) { 48 | margin-top: 0; 49 | } 50 | 51 | .menu ul.list li:nth-last-child(2) { 52 | background: none; 53 | } 54 | 55 | .menu ul.list li.title a { 56 | padding: 10px 15px; 57 | } 58 | 59 | .menu ul.list li.title a, 60 | .navbar a { 61 | color: #8d9ba8; 62 | text-decoration: none; 63 | font-size: 16px; 64 | font-weight: 300; 65 | } 66 | 67 | .menu ul.list li a { 68 | color: white; 69 | padding: 10px; 70 | font-weight: 300; 71 | text-decoration: none; 72 | } 73 | .menu ul.list li a.active { 74 | color: white; 75 | font-weight: bold; 76 | } 77 | 78 | .copyright { 79 | color: white; 80 | background: #000; 81 | } 82 | 83 | code { 84 | box-sizing: border-box; 85 | display: inline-block; 86 | padding: 0 5px; 87 | background: rgba(0, 148, 191, 0.1); 88 | border-radius: 3px; 89 | color: #0094bf; 90 | font-size: 13px; 91 | line-height: 20px; 92 | } 93 | 94 | pre { 95 | margin: 0; 96 | padding: 12px 12px; 97 | background: rgba(238, 238, 238, 0.35); 98 | border-radius: 3px; 99 | font-size: 13px; 100 | line-height: 1.5em; 101 | font-weight: 500; 102 | } 103 | 104 | .dark body { 105 | color: #fafafa; 106 | } 107 | .dark .content h1, 108 | .dark .content h2, 109 | .dark .content h3, 110 | .dark .content h4, 111 | .dark .content h5 { 112 | color: #fafafa; 113 | } 114 | 115 | .dark code { 116 | background: none; 117 | } 118 | 119 | .dark .content { 120 | color: #fafafa; 121 | } 122 | 123 | .dark .menu ul.list li.title a, 124 | .dark .navbar a { 125 | color: #8d9ba8; 126 | } 127 | 128 | .dark .menu ul.list li a { 129 | color: #fafafa; 130 | } 131 | -------------------------------------------------------------------------------- /jestSetup.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular/setup-jest'; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-device-detector-ws", 3 | "version": "10.1.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build:lib": "ng build ngx-device-detector --configuration production", 9 | "build:demo": "ng build demo --configuration production", 10 | "build:ci": "ng build demo --base-href https://ahsanayaz.github.io/ngx-device-detector/demo/ --configuration production --output-hashing=all", 11 | "docs": "npm run docs:build", 12 | "docs:build": "compodoc -p --tsconfig ./tsconfig.json -n ngx-device-detector -d docs --hideGenerator --theme Vagrant", 13 | "docs:serve": "npm run docs:build -- -s", 14 | "docs:watch": "npm run docs:build -- -s -w", 15 | "format:check:demo": "prettier --config ./.prettierrc --check \"projects/demo/**/*{.ts,.js,.html,.scss}\"", 16 | "format:check:lib": "prettier --config ./.prettierrc --check \"projects/ngx-device-detector/**/*{.ts,.js,.html,.scss}\"", 17 | "format:fix:demo": "prettier --config ./.prettierrc --write \"projects/demo/**/*{.ts,.js,.html,.scss}\"", 18 | "format:fix:lib": "prettier --config ./.prettierrc --write \"projects/ngx-device-detector/**/*{.ts,.js,.html,.scss}\"", 19 | "format:check:all": "prettier --config ./.prettierrc --check \"projects/**/*{.ts,.js,.html,.scss}\"", 20 | "format:fix:all": "prettier --config ./.prettierrc --write \"projects/**/*{.ts,.js,.html,.scss}\"", 21 | "lint": "npm run format:check:lib", 22 | "test": "jest", 23 | "test:coverage": "jest --coverage", 24 | "e2e": "ng e2e", 25 | "copy:readme": "cp README.md ./projects/ngx-device-detector", 26 | "changelog": "conventional-changelog -p angular -i CHANGE_LOG.md -s", 27 | "copy:changelog": "cp CHANGE_LOG.md ./dist/ngx-device-detector", 28 | "prerelease": "npm run changelog && npm run format:fix:lib && npm run copy:readme && npm run build:lib && npm run docs:build && npm run copy:changelog", 29 | "release": "cd dist/ngx-device-detector && npm publish && cd ../../", 30 | "deploy:ssr:demo": "sh scripts/deploy_ssr_demo.sh", 31 | "postversion": "node scripts/post-version", 32 | "bump:version": "node scripts/bump-version", 33 | "serve:ssr:demo": "node dist/demo/server/server.mjs" 34 | }, 35 | "repository": { 36 | "type": "git", 37 | "url": "https://github.com/AhsanAyaz/ngx-device-detector.git" 38 | }, 39 | "author": { 40 | "name": "Muhammad Ahsan Ayaz", 41 | "email": "ahsan.ubitian@gmail.com" 42 | }, 43 | "jest": { 44 | "preset": "jest-preset-angular", 45 | "setupFilesAfterEnv": [ 46 | "/jestSetup.ts" 47 | ], 48 | "testPathIgnorePatterns": [ 49 | "/node_modules/", 50 | "/dist/" 51 | ], 52 | "modulePathIgnorePatterns": [ 53 | "/dist/", 54 | "/projects/demo" 55 | ], 56 | "globals": { 57 | "ts-jest": { 58 | "tsconfig": "/tsconfig.spec.json", 59 | "stringifyContentPathRegex": "\\.html$" 60 | } 61 | } 62 | }, 63 | "keywords": [ 64 | "angular", 65 | "device", 66 | "device detector", 67 | "device detection", 68 | "ngx-device-detector", 69 | "angular device detector", 70 | "angular5", 71 | "angular6", 72 | "angular7", 73 | "angular8", 74 | "angular18", 75 | "angular19", 76 | "angular20" 77 | ], 78 | "bugs": { 79 | "url": "https://github.com/AhsanAyaz/ngx-device-detector.git/issues" 80 | }, 81 | "dependencies": { 82 | "@angular/animations": "~20.0.0", 83 | "@angular/common": "~20.0.0", 84 | "@angular/compiler": "~20.0.0", 85 | "@angular/core": "~20.0.0", 86 | "@angular/forms": "~20.0.0", 87 | "@angular/platform-browser": "~20.0.0", 88 | "@angular/platform-browser-dynamic": "~20.0.0", 89 | "@angular/platform-server": "~20.0.0", 90 | "@angular/router": "~20.0.0", 91 | "@angular/ssr": "^20.0.0", 92 | "conventional-changelog-cli": "~5.0.0", 93 | "express": "^4.18.2", 94 | "jest-preset-angular": "^14.5.5", 95 | "ngx-device-detector": "^8.0.0", 96 | "rxjs": "~7.8.1", 97 | "tslib": "~2.6.3", 98 | "zone.js": "~0.15.0" 99 | }, 100 | "devDependencies": { 101 | "@angular-devkit/build-angular": "~20.0.0", 102 | "@angular/cli": "~20.0.0", 103 | "@angular/compiler-cli": "~20.0.0", 104 | "@commitlint/cli": "~19.3.0", 105 | "@commitlint/config-angular": "~19.3.0", 106 | "@compodoc/compodoc": "~1.1.25", 107 | "@types/express": "^4.17.17", 108 | "@types/jest": "^29.5.12", 109 | "@types/node": "^20.14.2", 110 | "codelyzer": "~6.0.2", 111 | "husky": "~9.0.11", 112 | "jest": "~29.7.0", 113 | "jest-environment-jsdom": "^29.7.0", 114 | "jsdom": "^26.1.0", 115 | "ng-packagr": "~20.0.0", 116 | "prettier": "~3.3.2", 117 | "protractor": "~7.0.0", 118 | "semver": "~7.6.2", 119 | "ts-node": "~10.9.2", 120 | "tslint": "~6.1.0", 121 | "typescript": "~5.8.3" 122 | }, 123 | "husky": { 124 | "hooks": { 125 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 126 | } 127 | }, 128 | "overrides": { 129 | "ngx-device-detector": { 130 | "@angular/core": "$@angular/core", 131 | "@angular/common": "$@angular/common" 132 | } 133 | } 134 | } 135 | 136 | -------------------------------------------------------------------------------- /projects/demo/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: ['./src/**/*.e2e-spec.ts'], 13 | capabilities: { 14 | browserName: 'chrome', 15 | }, 16 | directConnect: true, 17 | baseUrl: 'http://localhost:4200/', 18 | framework: 'jasmine', 19 | jasmineNodeOpts: { 20 | showColors: true, 21 | defaultTimeoutInterval: 30000, 22 | print: function () {}, 23 | }, 24 | onPrepare() { 25 | require('ts-node').register({ 26 | project: require('path').join(__dirname, './tsconfig.json'), 27 | }); 28 | jasmine.getEnv().addReporter( 29 | new SpecReporter({ 30 | spec: { 31 | displayStacktrace: StacktraceOption.PRETTY, 32 | }, 33 | }) 34 | ); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /projects/demo/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display message saying app works', () => { 12 | page.navigateTo(); 13 | expect(page.getDemoHeadingText()).toEqual('ngx-device-detector'); 14 | }); 15 | 16 | it('should display device information in table', () => { 17 | page.navigateTo(); 18 | const infoElements = page.getDeviceInfoElements(); 19 | expect(infoElements.count()).toEqual(9); 20 | }); 21 | 22 | afterEach(async () => { 23 | // Assert that there are no errors emitted from the browser 24 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 25 | expect(logs).not.toContain( 26 | jasmine.objectContaining({ 27 | level: logging.Level.SEVERE, 28 | } as logging.Entry) 29 | ); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/demo/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element, ElementArrayFinder } from 'protractor'; 2 | import { promise as wdpromise } from 'selenium-webdriver'; 3 | 4 | export class AppPage { 5 | navigateTo(): wdpromise.Promise { 6 | return browser.get('/'); 7 | } 8 | 9 | getDemoHeadingText(): wdpromise.Promise { 10 | return element(by.css('app-root .demo-heading-text')).getText(); 11 | } 12 | 13 | getDeviceInfoElements(): ElementArrayFinder { 14 | return element.all(by.css('app-root .information-table .info-item')); 15 | } 16 | 17 | getParagraphText(): wdpromise.Promise { 18 | return element(by.css('app-root h1')).getText(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /projects/demo/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../../out-tsc/e2e", 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "types": ["jasmine", "jasminewd2", "node"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/demo/server.ts: -------------------------------------------------------------------------------- 1 | import { APP_BASE_HREF } from '@angular/common'; 2 | import { CommonEngine } from '@angular/ssr/node'; 3 | import express from 'express'; 4 | import { fileURLToPath } from 'node:url'; 5 | import { dirname, join, resolve } from 'node:path'; 6 | import AppServerModule from './src/main.server'; 7 | 8 | // The Express app is exported so that it can be used by serverless Functions. 9 | export function app(): express.Express { 10 | const server = express(); 11 | const serverDistFolder = dirname(fileURLToPath(import.meta.url)); 12 | const browserDistFolder = resolve(serverDistFolder, '../browser'); 13 | const indexHtml = join(serverDistFolder, 'index.server.html'); 14 | 15 | const commonEngine = new CommonEngine(); 16 | 17 | server.set('view engine', 'html'); 18 | server.set('views', browserDistFolder); 19 | 20 | // Example Express Rest API endpoints 21 | // server.get('/api/**', (req, res) => { }); 22 | // Serve static files from /browser 23 | server.get('**', express.static(browserDistFolder, { 24 | maxAge: '1y', 25 | index: 'index.html', 26 | })); 27 | 28 | // All regular routes use the Angular engine 29 | server.get('**', (req, res, next) => { 30 | const { protocol, originalUrl, baseUrl, headers } = req; 31 | 32 | commonEngine 33 | .render({ 34 | bootstrap: AppServerModule, 35 | documentFilePath: indexHtml, 36 | url: `${protocol}://${headers.host}${originalUrl}`, 37 | publicPath: browserDistFolder, 38 | providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], 39 | }) 40 | .then((html) => res.send(html)) 41 | .catch((err) => next(err)); 42 | }); 43 | 44 | return server; 45 | } 46 | 47 | function run(): void { 48 | const port = process.env['PORT'] || 4000; 49 | 50 | // Start up the Node server 51 | const server = app(); 52 | server.listen(port, () => { 53 | console.log(`Node Express server listening on http://localhost:${port}`); 54 | }); 55 | } 56 | 57 | run(); 58 | -------------------------------------------------------------------------------- /projects/demo/server/Procfile: -------------------------------------------------------------------------------- 1 | web: node server/main.js 2 | -------------------------------------------------------------------------------- /projects/demo/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-device-detector-ssr", 3 | "version": "0.0.1", 4 | "description": "Ngx Device Detector Demo SSR", 5 | "engines": { 6 | "node": "12.x" 7 | }, 8 | "main": "server/main.js", 9 | "scripts": { 10 | "start": "node server/main.js" 11 | }, 12 | "dependencies": { 13 | "@nguniversal/express-engine": "^10.0.2", 14 | "express": "^4.15.2" 15 | }, 16 | "keywords": [ 17 | "node", 18 | "heroku", 19 | "express" 20 | ], 21 | "license": "MIT" 22 | } 23 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
                2 |
                3 | 6 |
                7 |
                8 | 9 |
                10 | 11 |

                ngx-device-detector

                12 |
                13 | 14 |

                15 | An Angular 5+ powered AOT compatible device detector that helps to identify browser, os and other useful 16 | information regarding the device using the app. The processing is based on user-agent. 17 |

                18 | 19 |

                20 | build status 25 | npm version 28 | github stars 33 | license 36 |

                37 |
                38 |
                39 |
                Demo is at Angular version = {{ version }}
                40 |
                41 |

                Open this page from different devices to see the appropriate details

                42 |

                Device Information

                43 |
                44 |
                45 |
                46 |
                47 | 48 | 49 | Paste the user agent (window.navigator.userAgent) here 50 |
                51 |
                52 |
                53 | 56 | 57 |
                58 |
                59 |
                60 |
                61 |
                62 | @if (isMobile) { 63 |
                Mobile
                64 | } 65 | @if (isTablet) { 66 |
                Tablet
                67 | } 68 | @if (isDesktop) { 69 |
                Desktop
                70 | } 71 |
                72 | 73 | 74 | 75 | 76 | 77 | @for (info of deviceInfo | keys: propsToShow; track info) { 78 | 79 | 80 | 81 | 82 | } 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
                PropertyValue
                {{ info.key }}{{ info.value }}
                  
                isDesktop(){{ isDesktop }}
                isMobile(){{ isMobile }}
                isTablet(){{ isTablet }}
                100 |
                101 |
                102 |
                103 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | position: relative; 3 | display: block; 4 | } 5 | #demoApp { 6 | max-width: 600px; 7 | padding-top: 40px; 8 | .demo-container { 9 | .github-logo { 10 | cursor: pointer; 11 | position: absolute; 12 | top: 10px; 13 | right: 10px; 14 | img { 15 | width: 40px; 16 | transform: rotate(45deg); 17 | } 18 | &:hover { 19 | img { 20 | opacity: 0.6; 21 | } 22 | } 23 | } 24 | .badges { 25 | a { 26 | margin-left: 4px; 27 | margin-right: 4px; 28 | } 29 | } 30 | } 31 | 32 | .device-icon { 33 | text-align: center; 34 | padding: 20px; 35 | 36 | .fa { 37 | font-size: 100px; 38 | } 39 | } 40 | 41 | .user-agent { 42 | display: flex; 43 | flex-direction: column; 44 | justify-content: flex-end; 45 | &__input { 46 | flex: 1; 47 | input { 48 | width: 100%; 49 | } 50 | } 51 | &__buttons { 52 | display: flex; 53 | justify-content: flex-end; 54 | > button { 55 | &:first-child { 56 | margin-right: 10px; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing'; 2 | import { DeviceDetectorService } from 'ngx-device-detector'; 3 | import { AppComponent } from './app.component'; 4 | import { KeysPipe } from './pipes/keys.pipe'; 5 | 6 | describe('AppComponent', () => { 7 | let fixture: ComponentFixture; 8 | let app; 9 | 10 | beforeEach(waitForAsync(() => { 11 | TestBed.configureTestingModule({ 12 | imports: [], 13 | declarations: [AppComponent, KeysPipe], 14 | providers: [DeviceDetectorService], 15 | }).compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(AppComponent); 20 | app = fixture.debugElement.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create the app', waitForAsync(() => { 25 | expect(app).toBeTruthy(); 26 | })); 27 | 28 | it('should render demo heading in an element having class demo-heading-text', waitForAsync(() => { 29 | const compiled = fixture.debugElement.nativeElement; 30 | expect(compiled.querySelector('.demo-heading-text').textContent).toContain('ngx-device-detector'); 31 | })); 32 | 33 | it('should render device information inside table in tags ', waitForAsync(() => { 34 | const compiled = fixture.debugElement.nativeElement; 35 | expect(compiled.querySelectorAll('table tr.info-item').length).toEqual(11); // all the 6 required properties 36 | })); 37 | }); 38 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, VERSION, inject } from '@angular/core'; 2 | import { DeviceDetectorService } from 'ngx-device-detector'; 3 | import { NgClass } from '@angular/common'; 4 | import { KeysPipe } from './pipes/keys.pipe'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.scss'], 10 | imports: [NgClass, KeysPipe], 11 | standalone: true 12 | }) 13 | export class AppComponent { 14 | private deviceService = inject(DeviceDetectorService); 15 | 16 | propsToShow = ['userAgent', 'os', 'browser', 'device', 'os_version', 'browser_version', 'deviceType', 'orientation', 'isDesktopMode']; 17 | deviceInfo = null; 18 | version = VERSION.full; 19 | userAgentInputVal = null; 20 | ua; 21 | 22 | /** Inserted by Angular inject() migration for backwards compatibility */ 23 | constructor(...args: unknown[]); 24 | constructor() { 25 | try { 26 | this.ua = window.navigator.userAgent; 27 | } catch { 28 | console.log('UA not available'); 29 | } 30 | this.applyDevice(); 31 | } 32 | 33 | getDeviceInfo(): void { 34 | this.deviceInfo = this.deviceService.getDeviceInfo(); 35 | } 36 | 37 | get isMobile(): boolean { 38 | return this.deviceService.isMobile(); 39 | } 40 | 41 | get isTablet(): boolean { 42 | return this.deviceService.isTablet(); 43 | } 44 | 45 | get isDesktop(): boolean { 46 | return this.deviceService.isDesktop(); 47 | } 48 | 49 | get isDesktopMode(): boolean { 50 | return this.deviceService.isDesktopModeEnabled(); 51 | } 52 | 53 | applyDevice(userAgent = this.ua): void { 54 | this.deviceService.setDeviceInfo(userAgent); 55 | this.getDeviceInfo(); 56 | } 57 | 58 | resetDeviceInfo(): void { 59 | this.applyDevice(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.config.server.ts: -------------------------------------------------------------------------------- 1 | import { provideServerRendering } from '@angular/ssr'; 2 | import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; 3 | import { appConfig } from './app.config'; 4 | 5 | const serverConfig: ApplicationConfig = { 6 | providers: [ 7 | provideServerRendering() 8 | ] 9 | }; 10 | 11 | export const config = mergeApplicationConfig(appConfig, serverConfig); 12 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationConfig, 3 | } from '@angular/core'; 4 | 5 | export const appConfig: ApplicationConfig = { 6 | providers: [ 7 | 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /projects/demo/src/app/pipes/keys.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { KeysPipe } from './keys.pipe'; 2 | 3 | describe('KeysPipe', () => { 4 | it('create an instance', () => { 5 | const pipe = new KeysPipe(); 6 | expect(pipe).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /projects/demo/src/app/pipes/keys.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'keys', standalone:true }) 4 | export class KeysPipe implements PipeTransform { 5 | transform(value, props: string[] = []): any { 6 | const keys = []; 7 | const noFilter = props.length === 0; 8 | for (const key in value) { 9 | if (noFilter) { 10 | if (value.hasOwnProperty(key)) { 11 | keys.push({ key, value: value[key] }); 12 | } 13 | } else { 14 | if (props.indexOf(key) !== -1) { 15 | keys.push({ key, value: value[key] }); 16 | } 17 | } 18 | } 19 | return keys; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /projects/demo/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/projects/demo/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/demo/src/assets/images/github-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/projects/demo/src/assets/images/github-logo.png -------------------------------------------------------------------------------- /projects/demo/src/express.tokens.ts: -------------------------------------------------------------------------------- 1 | 2 | import { InjectionToken } from '@angular/core'; 3 | import { Request, Response } from 'express'; 4 | 5 | export const REQUEST = new InjectionToken('REQUEST'); 6 | export const RESPONSE = new InjectionToken('RESPONSE'); 7 | -------------------------------------------------------------------------------- /projects/demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhsanAyaz/ngx-device-detector/318043e922276384257138e2bee2a7242b671f10/projects/demo/src/favicon.ico -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /projects/demo/src/main.server.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { AppComponent } from './app/app.component'; 3 | import { config } from './app/app.config.server'; 4 | 5 | const bootstrap = () => bootstrapApplication(AppComponent, config); 6 | 7 | export default bootstrap; 8 | -------------------------------------------------------------------------------- /projects/demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | 4 | import { APP_ID, importProvidersFrom } from '@angular/core'; 5 | import { provideNoopAnimations } from '@angular/platform-browser/animations'; 6 | import { CommonModule } from '@angular/common'; 7 | import { AppComponent } from './app/app.component'; 8 | import { bootstrapApplication } from '@angular/platform-browser'; 9 | 10 | document.addEventListener('DOMContentLoaded', () => { 11 | bootstrapApplication(AppComponent, { 12 | providers: [ 13 | importProvidersFrom(CommonModule), 14 | { 15 | provide: APP_ID, 16 | useValue: 'serverApp' 17 | }, 18 | provideNoopAnimations(), 19 | ] 20 | }) 21 | .catch(err => console.error(err)); 22 | }); 23 | -------------------------------------------------------------------------------- /projects/demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ // Run `npm install --save classlist.js`. // Run `npm install --save web-animations-js`. 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | /*************************************************************************************************** 51 | * APPLICATION IMPORTS 52 | */ 53 | -------------------------------------------------------------------------------- /projects/demo/src/server.ts: -------------------------------------------------------------------------------- 1 | import { APP_BASE_HREF } from '@angular/common'; 2 | import { CommonEngine, isMainModule } from '@angular/ssr/node'; 3 | import express from 'express'; 4 | import { dirname, join, resolve } from 'node:path'; 5 | import { fileURLToPath } from 'node:url'; 6 | import bootstrap from './main.server'; 7 | 8 | const serverDistFolder = dirname(fileURLToPath(import.meta.url)); 9 | const browserDistFolder = resolve(serverDistFolder, '../browser'); 10 | const indexHtml = join(serverDistFolder, 'index.server.html'); 11 | 12 | const app = express(); 13 | const commonEngine = new CommonEngine(); 14 | 15 | /** 16 | * Example Express Rest API endpoints can be defined here. 17 | * Uncomment and define endpoints as necessary. 18 | * 19 | * Example: 20 | * ```ts 21 | * app.get('/api/**', (req, res) => { 22 | * // Handle API request 23 | * }); 24 | * ``` 25 | */ 26 | 27 | /** 28 | * Serve static files from /browser 29 | */ 30 | app.get( 31 | '**', 32 | express.static(browserDistFolder, { 33 | maxAge: '1y', 34 | index: 'index.html' 35 | }), 36 | ); 37 | 38 | /** 39 | * Handle all other requests by rendering the Angular application. 40 | */ 41 | app.get('**', (req, res, next) => { 42 | const { protocol, originalUrl, baseUrl, headers } = req; 43 | 44 | commonEngine 45 | .render({ 46 | bootstrap, 47 | documentFilePath: indexHtml, 48 | url: `${protocol}://${headers.host}${originalUrl}`, 49 | publicPath: browserDistFolder, 50 | providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], 51 | }) 52 | .then((html) => res.send(html)) 53 | .catch((err) => next(err)); 54 | }); 55 | 56 | /** 57 | * Start the server if this module is the main entry point. 58 | * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000. 59 | */ 60 | if (isMainModule(import.meta.url)) { 61 | const port = process.env['PORT'] || 4000; 62 | app.listen(port, () => { 63 | console.log(`Node Express server listening on http://localhost:${port}`); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /projects/demo/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/app", 6 | "types": [ 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/main.ts", 12 | "src/polyfills.ts", 13 | "server.ts", 14 | "src/server.ts" 15 | ], 16 | "include": [ 17 | "src/**/*.d.ts" 18 | ] 19 | } -------------------------------------------------------------------------------- /projects/demo/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [true, "attribute", "app", "camelCase"], 5 | "component-selector": [true, "element", "app", "kebab-case"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/README.md: -------------------------------------------------------------------------------- 1 |

                2 | 3 |

                4 | 5 | 6 |

                ngx-device-detector

                7 |
                8 | 9 |

                10 | An Angular 6+ powered AOT compatible device detector that helps to identify browser, os and other useful information regarding the device using the app. The processing is based on user-agent. This library was built at KoderLabs, which is one of the best places I've worked at ❤️ 11 |

                12 | 13 |

                14 | build status 15 | npm version 16 | license 17 | stars 18 | 19 |

                20 |

                21 | Deprecated package : 22 | npm downloads total 23 | npm downloads/month 24 |

                25 |

                26 | New package : 27 | npm downloads total 28 | npm downloads/month

                29 | If you use Angular 5, you must use v1.5.2 or earlier 30 |

                31 | 32 | ## DOCS 33 | 34 | [Ngx Device Detector DOCS](https://ahsanayaz.github.io/ngx-device-detector) 35 | 36 | ## Live DEMO 37 | 38 | [Regular Demo](https://ahsanayaz.github.io/ngx-device-detector/demo) 39 | 40 | 41 | 42 | ## Dependencies 43 | 44 | Latest version available for each version of Angular 45 | 46 | | ngx-device-detector | Angular | 47 | | ------------------- | ------- | 48 | | 1.3.3 | 7.x | 49 | | 1.3.5 | 8.x | 50 | | 1.4.1 | 9.x | 51 | | 1.4.5 | 10.x | 52 | | 2.0.5 | 11.x | 53 | | 2.1.0 | 12.x | 54 | | 3.x.x | 13.x | 55 | | 4.x.x | 14.x | 56 | | 5.x.x | 15.x | 57 | | 6.x.x | 16.x | 58 | | 7.x.x | 17.x | 59 | | 8.x.x | 18.x | 60 | | 9.x.x | 19.x | 61 | | 10.x.x | 20.x | 62 | 63 | ## Installation 64 | 65 | To install this library, run: 66 | 67 | ```bash 68 | $ npm install ngx-device-detector --save 69 | ``` 70 | 71 | In your component where you want to use the Device Service 72 | 73 | ```typescript 74 | import { Component } from '@angular/core'; 75 | ... 76 | import { DeviceDetectorService } from 'ngx-device-detector'; 77 | ... 78 | @Component({ 79 | selector: 'home', // 80 | styleUrls: [ './home.component.scss' ], 81 | templateUrl: './home.component.html', 82 | ... 83 | }) 84 | 85 | export class HomeComponent { 86 | deviceInfo = null; 87 | ... 88 | constructor(..., private http: Http, private deviceService: DeviceDetectorService) { 89 | this.epicFunction(); 90 | } 91 | ... 92 | epicFunction() { 93 | console.log('hello `Home` component'); 94 | this.deviceInfo = this.deviceService.getDeviceInfo(); 95 | const isMobile = this.deviceService.isMobile(); 96 | const isTablet = this.deviceService.isTablet(); 97 | const isDesktopDevice = this.deviceService.isDesktop(); 98 | console.log(this.deviceInfo); 99 | console.log(isMobile); // returns if the device is a mobile device (android / iPhone / windows-phone etc) 100 | console.log(isTablet); // returns if the device us a tablet (iPad etc) 101 | console.log(isDesktopDevice); // returns if the app is running on a Desktop browser. 102 | } 103 | ... 104 | } 105 | 106 | ``` 107 | 108 | For SSR, you have to make sure that the User Agent is available for device detection. I.e. you'll need to provide it manually. If using ExpressJS for example: 109 | 110 | **express.tokens.ts** 111 | 112 | ```typescript 113 | import { InjectionToken } from '@angular/core'; 114 | import { Request, Response } from 'express'; 115 | 116 | export const REQUEST = new InjectionToken('REQUEST'); 117 | export const RESPONSE = new InjectionToken('RESPONSE'); 118 | ``` 119 | 120 | **universal-device-detector.service.ts:** 121 | 122 | ```typescript 123 | import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core'; 124 | import { REQUEST } from 'path/to/express.tokens'; 125 | import { Request } from 'express'; 126 | import { DeviceDetectorService } from 'ngx-device-detector'; 127 | import { isPlatformServer } from '@angular/common'; 128 | 129 | @Injectable() 130 | export class UniversalDeviceDetectorService extends DeviceDetectorService { 131 | constructor(@Inject(PLATFORM_ID) platformId: any, @Optional() @Inject(REQUEST) request: Request) { 132 | super(platformId); 133 | if (isPlatformServer(platformId)) { 134 | super.setDeviceInfo((request.headers['user-agent'] as string) || ''); 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | **app.server.module.ts:** 141 | 142 | ```typescript 143 | { 144 | provide: DeviceDetectorService, 145 | useClass: UniversalDeviceDetectorService 146 | }, 147 | ``` 148 | 149 | ## Device service 150 | 151 | Holds the following properties 152 | 153 | - browser 154 | - os 155 | - device 156 | - userAgent 157 | - os_version 158 | 159 | ## Helper Methods 160 | 161 | - **isMobile() :** returns if the device is a mobile device (android / iPhone/ windows-phone etc) 162 | - **isTablet() :** returns if the device us a tablet (iPad etc) 163 | - **isDesktop() :** returns if the app is running on a Desktop browser. 164 | 165 | ## Development 166 | 167 | To generate all `*.js`, `*.js.map` and `*.d.ts` files: 168 | 169 | ```bash 170 | $ npm run tsc 171 | ``` 172 | 173 | To lint all `*.ts` files: 174 | 175 | ```bash 176 | $ npm run lint 177 | ``` 178 | 179 | To run unit tests 180 | 181 | ```bash 182 | $ npm run test 183 | ``` 184 | 185 | To build the library 186 | 187 | ```bash 188 | $ npm run build 189 | ``` 190 | 191 | ## Run the DEMO 192 | 193 | Make sure you have @angular/cli installed 194 | 195 | ```bash 196 | $ npm install -g @angular/cli 197 | ``` 198 | 199 | ```bash 200 | $ cd demo 201 | $ npm install 202 | $ ng serve 203 | ``` 204 | 205 | the demo will be up at `localhost:4200` 206 | 207 | ## Change Log 208 | 209 | Please see [CHANGE_LOG.md](CHANGE_LOG.md) for the updates. 210 | 211 | ## Credits 212 | 213 | The library is inspired by and based on the work from [ng-device-detector ](https://github.com/srfrnk/ng-device-detector). Also used a typescript wrapper of the amazing work in [ReTree](https://github.com/srfrnk/re-tree) for regex based needs and an Angular2 Library Creator boilerplate to get the work started fast. I.e. [Generator Angular2 library](https://github.com/jvandemo/generator-angular2-library). 214 | 215 | ## License 216 | 217 | [MIT](https://github.com/AhsanAyaz/ngx-device-detector/blob/master/LICENSE) 218 | 219 | ## Need help with your Angular/Web projects 220 | 221 | Write to us at [IOMechs](mailto:info@iomechs.com) 222 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-device-detector", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-device-detector", 3 | "version": "10.1.0", 4 | "license": "MIT", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/AhsanAyaz/ngx-device-detector.git" 8 | }, 9 | "author": { 10 | "name": "Muhammad Ahsan Ayaz", 11 | "email": "ahsan.ubitian@gmail.com" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "device", 16 | "device detector", 17 | "device detection", 18 | "ngx-device-detector", 19 | "angular device detector", 20 | "angular5", 21 | "angular6", 22 | "angular7", 23 | "angular8", 24 | "angular18", 25 | "angular19", 26 | "angular20" 27 | ], 28 | "bugs": { 29 | "url": "https://github.com/AhsanAyaz/ngx-device-detector.git/issues" 30 | }, 31 | "peerDependencies": { 32 | "@angular/common": "^20.0.0", 33 | "@angular/core": "^20.0.0" 34 | }, 35 | "dependencies": { 36 | "tslib": "^2.6.3" 37 | } 38 | } -------------------------------------------------------------------------------- /projects/ngx-device-detector/src/lib/retree.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by ahsanayaz on 08/11/2016. 3 | */ 4 | 5 | export class ReTree { 6 | private regexCache = new Map(); 7 | 8 | constructor() {} 9 | 10 | private getRegex(pattern: string): RegExp { 11 | if (!this.regexCache.has(pattern)) { 12 | this.regexCache.set(pattern, new RegExp(pattern)); 13 | } 14 | return this.regexCache.get(pattern)!; 15 | } 16 | 17 | public test(str: string, regex: any): any { 18 | if (typeof regex === 'string') { 19 | regex = this.getRegex(regex); 20 | } 21 | 22 | if (regex instanceof RegExp) { 23 | return regex.test(str); 24 | } else if (regex && Array.isArray(regex.and)) { 25 | return regex.and.every((item: any) => { 26 | return this.test(str, item); 27 | }); 28 | } else if (regex && Array.isArray(regex.or)) { 29 | return regex.or.some((item: any) => { 30 | return this.test(str, item); 31 | }); 32 | } else if (regex && regex.not) { 33 | return !this.test(str, regex.not); 34 | } else { 35 | return false; 36 | } 37 | } 38 | 39 | public exec(str: string, regex: any): any { 40 | if (typeof regex === 'string') { 41 | regex = this.getRegex(regex); 42 | } 43 | 44 | if (regex instanceof RegExp) { 45 | return regex.exec(str); 46 | } else if (regex && Array.isArray(regex)) { 47 | return regex.reduce((res: any, item: any) => { 48 | return !!res ? res : this.exec(str, item); 49 | }, null); 50 | } else { 51 | return null; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-device-detector 3 | */ 4 | 5 | export * from './lib/device-detector.constants'; 6 | export * from './lib/device-detector.service'; 7 | export * from './lib/retree'; 8 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declarationMap": true, 7 | "declaration": true, 8 | "inlineSources": true, 9 | "types": [], 10 | "lib": ["dom", "es2018"] 11 | }, 12 | "angularCompilerOptions": { 13 | "skipTemplateCodegen": true, 14 | "strictMetadataEmit": true, 15 | "enableResourceInlining": true 16 | }, 17 | "exclude": ["**/*.spec.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-device-detector/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [true, "attribute", "lib", "camelCase"], 5 | "component-selector": [true, "element", "lib", "kebab-case"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scripts/bump-version.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'); 2 | let semver = require('semver'); 3 | 4 | const bumpFileVersion = filePath => { 5 | if (!fs.existsSync(filePath)) { 6 | console.error(`Where is the package.json (${filePath})? You high bro??`); 7 | return; 8 | } 9 | let package = fs.readFileSync(filePath); 10 | package = JSON.parse(package); 11 | let currentVersion = package.version; 12 | let targetVersion = process.argv[2]; 13 | const isNotBump = !['major', 'minor', 'patch'].includes(targetVersion); 14 | const isNotVersion = !packageVersionRegex.test(targetVersion); 15 | if (isNotBump && isNotVersion) { 16 | console.error('Invalid version/patch type provided'); 17 | return; 18 | } 19 | const newVersion = isNotVersion ? semver.inc(package.version, targetVersion) : targetVersion; 20 | package.version = newVersion; 21 | fs.writeFileSync(filePath, JSON.stringify(package, null, 2)); 22 | console.log('Version updated', currentVersion, '=>', newVersion); 23 | }; 24 | 25 | const dDPackageJson = 'projects/ngx-device-detector/package.json'; 26 | const wsPackageJson = 'package.json'; 27 | const wsPackageLockJson = 'package-lock.json'; 28 | const packageVersionRegex = /^\d{1,2}\.\d{1,2}\.\d{1,2}$/; 29 | 30 | const filesToBump = [wsPackageJson, wsPackageLockJson, dDPackageJson]; 31 | 32 | filesToBump.map(filePath => bumpFileVersion(filePath)); 33 | -------------------------------------------------------------------------------- /scripts/deploy_ssr_demo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash -e 2 | 3 | git subtree split --prefix dist/demo -b temp 4 | git push -f heroku temp:master 5 | git branch -D temp 6 | -------------------------------------------------------------------------------- /steps-to-release.md: -------------------------------------------------------------------------------- 1 | # Creating Releases 2 | 3 | Make sure that you're logged in to NPM in the terminal via `npm login`. 4 | 5 | 1. `npm run bump:version ` 6 | 2. `npm run release` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "esModuleInterop": true, 8 | "declaration": false, 9 | "experimentalDecorators": true, 10 | "moduleResolution": "bundler", 11 | "importHelpers": true, 12 | "target": "ES2022", 13 | "module": "es2020", 14 | "lib": [ 15 | "es2018", 16 | "dom" 17 | ], 18 | "paths": { 19 | "ngx-device-detector": [ 20 | "dist/ngx-device-detector/ngx-device-detector", 21 | "dist/ngx-device-detector" 22 | ] 23 | }, 24 | "useDefineForClassFields": false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": ["jest", "node"], 6 | "esModuleInterop": true 7 | }, 8 | "files": ["projects/demo/src/polyfills.ts"], 9 | "include": ["projects/**/src/**/*.spec.ts", "projects/**/src/**/*.d.ts"], 10 | "exclude": ["./dist"] 11 | } 12 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "align": { 8 | "options": [ 9 | "parameters", 10 | "statements" 11 | ] 12 | }, 13 | "array-type": false, 14 | "arrow-return-shorthand": true, 15 | "curly": true, 16 | "deprecation": { 17 | "severity": "warning" 18 | }, 19 | "eofline": true, 20 | "import-blacklist": [ 21 | true, 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": { 26 | "options": [ 27 | "spaces" 28 | ] 29 | }, 30 | "max-classes-per-file": false, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-console": [ 47 | true, 48 | "debug", 49 | "info", 50 | "time", 51 | "timeEnd", 52 | "trace" 53 | ], 54 | "no-empty": false, 55 | "no-inferrable-types": [ 56 | true, 57 | "ignore-params" 58 | ], 59 | "no-non-null-assertion": true, 60 | "no-redundant-jsdoc": true, 61 | "no-switch-case-fall-through": true, 62 | "no-var-requires": false, 63 | "object-literal-key-quotes": [ 64 | true, 65 | "as-needed" 66 | ], 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "semicolon": { 72 | "options": [ 73 | "always" 74 | ] 75 | }, 76 | "space-before-function-paren": { 77 | "options": { 78 | "anonymous": "never", 79 | "asyncArrow": "always", 80 | "constructor": "never", 81 | "method": "never", 82 | "named": "never" 83 | } 84 | }, 85 | "typedef": [ 86 | true, 87 | "call-signature" 88 | ], 89 | "typedef-whitespace": { 90 | "options": [ 91 | { 92 | "call-signature": "nospace", 93 | "index-signature": "nospace", 94 | "parameter": "nospace", 95 | "property-declaration": "nospace", 96 | "variable-declaration": "nospace" 97 | }, 98 | { 99 | "call-signature": "onespace", 100 | "index-signature": "onespace", 101 | "parameter": "onespace", 102 | "property-declaration": "onespace", 103 | "variable-declaration": "onespace" 104 | } 105 | ] 106 | }, 107 | "variable-name": { 108 | "options": [ 109 | "ban-keywords", 110 | "check-format", 111 | "allow-pascal-case" 112 | ] 113 | }, 114 | "whitespace": { 115 | "options": [ 116 | "check-branch", 117 | "check-decl", 118 | "check-operator", 119 | "check-separator", 120 | "check-type", 121 | "check-typecast" 122 | ] 123 | }, 124 | "component-class-suffix": true, 125 | "contextual-lifecycle": true, 126 | "directive-class-suffix": true, 127 | "no-conflicting-lifecycle": true, 128 | "no-host-metadata-property": true, 129 | "no-input-rename": true, 130 | "no-inputs-metadata-property": true, 131 | "no-output-native": true, 132 | "no-output-on-prefix": true, 133 | "no-output-rename": true, 134 | "no-outputs-metadata-property": true, 135 | "template-banana-in-box": true, 136 | "template-no-negated-async": true, 137 | "use-lifecycle-interface": true, 138 | "use-pipe-transform-interface": true 139 | } 140 | } 141 | --------------------------------------------------------------------------------