├── .gitattributes
├── .gitignore
├── BrowserDetectDemo
├── App.razor
├── BrowserDetectDemo.csproj
├── Pages
│ └── Index.razor
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Shared
│ ├── MainLayout.razor
│ ├── MainLayout.razor.css
│ ├── NavMenu.razor
│ └── NavMenu.razor.css
├── _Imports.razor
└── wwwroot
│ ├── css
│ ├── app.css
│ ├── bootstrap
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.css.map
│ └── open-iconic
│ │ ├── FONT-LICENSE
│ │ ├── ICON-LICENSE
│ │ ├── README.md
│ │ └── font
│ │ ├── css
│ │ └── open-iconic-bootstrap.min.css
│ │ └── fonts
│ │ ├── open-iconic.eot
│ │ ├── open-iconic.otf
│ │ ├── open-iconic.svg
│ │ ├── open-iconic.ttf
│ │ └── open-iconic.woff
│ ├── favicon.ico
│ ├── icon-192.png
│ ├── icon-512.png
│ ├── images
│ └── psc_logo.png
│ ├── index.html
│ ├── manifest.json
│ ├── service-worker.js
│ └── service-worker.published.js
├── PSC.Blazor.Components.BrowserDetect.sln
├── README.md
└── src
├── BrowserDetect.razor
├── BrowserDetectJsInterop.cs
├── Models
└── BrowserDetect.cs
├── PSC.Blazor.Components.BrowserDetect.csproj
├── _Imports.razor
├── psc_logo.ico
├── psc_logo.png
└── wwwroot
└── js
└── browserDetect.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.razor linguist-language=C#
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.vspscc
94 | *.vssscc
95 | .builds
96 | *.pidb
97 | *.svclog
98 | *.scc
99 |
100 | # Chutzpah Test files
101 | _Chutzpah*
102 |
103 | # Visual C++ cache files
104 | ipch/
105 | *.aps
106 | *.ncb
107 | *.opendb
108 | *.opensdf
109 | *.sdf
110 | *.cachefile
111 | *.VC.db
112 | *.VC.VC.opendb
113 |
114 | # Visual Studio profiler
115 | *.psess
116 | *.vsp
117 | *.vspx
118 | *.sap
119 |
120 | # Visual Studio Trace Files
121 | *.e2e
122 |
123 | # TFS 2012 Local Workspace
124 | $tf/
125 |
126 | # Guidance Automation Toolkit
127 | *.gpState
128 |
129 | # ReSharper is a .NET coding add-in
130 | _ReSharper*/
131 | *.[Rr]e[Ss]harper
132 | *.DotSettings.user
133 |
134 | # TeamCity is a build add-in
135 | _TeamCity*
136 |
137 | # DotCover is a Code Coverage Tool
138 | *.dotCover
139 |
140 | # AxoCover is a Code Coverage Tool
141 | .axoCover/*
142 | !.axoCover/settings.json
143 |
144 | # Coverlet is a free, cross platform Code Coverage Tool
145 | coverage*.json
146 | coverage*.xml
147 | coverage*.info
148 |
149 | # Visual Studio code coverage results
150 | *.coverage
151 | *.coveragexml
152 |
153 | # NCrunch
154 | _NCrunch_*
155 | .*crunch*.local.xml
156 | nCrunchTemp_*
157 |
158 | # MightyMoose
159 | *.mm.*
160 | AutoTest.Net/
161 |
162 | # Web workbench (sass)
163 | .sass-cache/
164 |
165 | # Installshield output folder
166 | [Ee]xpress/
167 |
168 | # DocProject is a documentation generator add-in
169 | DocProject/buildhelp/
170 | DocProject/Help/*.HxT
171 | DocProject/Help/*.HxC
172 | DocProject/Help/*.hhc
173 | DocProject/Help/*.hhk
174 | DocProject/Help/*.hhp
175 | DocProject/Help/Html2
176 | DocProject/Help/html
177 |
178 | # Click-Once directory
179 | publish/
180 |
181 | # Publish Web Output
182 | *.[Pp]ublish.xml
183 | *.azurePubxml
184 | # Note: Comment the next line if you want to checkin your web deploy settings,
185 | # but database connection strings (with potential passwords) will be unencrypted
186 | *.pubxml
187 | *.publishproj
188 |
189 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
190 | # checkin your Azure Web App publish settings, but sensitive information contained
191 | # in these scripts will be unencrypted
192 | PublishScripts/
193 |
194 | # NuGet Packages
195 | *.nupkg
196 | # NuGet Symbol Packages
197 | *.snupkg
198 | # The packages folder can be ignored because of Package Restore
199 | **/[Pp]ackages/*
200 | # except build/, which is used as an MSBuild target.
201 | !**/[Pp]ackages/build/
202 | # Uncomment if necessary however generally it will be regenerated when needed
203 | #!**/[Pp]ackages/repositories.config
204 | # NuGet v3's project.json files produces more ignorable files
205 | *.nuget.props
206 | *.nuget.targets
207 |
208 | # Microsoft Azure Build Output
209 | csx/
210 | *.build.csdef
211 |
212 | # Microsoft Azure Emulator
213 | ecf/
214 | rcf/
215 |
216 | # Windows Store app package directories and files
217 | AppPackages/
218 | BundleArtifacts/
219 | Package.StoreAssociation.xml
220 | _pkginfo.txt
221 | *.appx
222 | *.appxbundle
223 | *.appxupload
224 |
225 | # Visual Studio cache files
226 | # files ending in .cache can be ignored
227 | *.[Cc]ache
228 | # but keep track of directories ending in .cache
229 | !?*.[Cc]ache/
230 |
231 | # Others
232 | ClientBin/
233 | ~$*
234 | *~
235 | *.dbmdl
236 | *.dbproj.schemaview
237 | *.jfm
238 | *.pfx
239 | *.publishsettings
240 | orleans.codegen.cs
241 |
242 | # Including strong name files can present a security risk
243 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
244 | #*.snk
245 |
246 | # Since there are multiple workflows, uncomment next line to ignore bower_components
247 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
248 | #bower_components/
249 |
250 | # RIA/Silverlight projects
251 | Generated_Code/
252 |
253 | # Backup & report files from converting an old project file
254 | # to a newer Visual Studio version. Backup files are not needed,
255 | # because we have git ;-)
256 | _UpgradeReport_Files/
257 | Backup*/
258 | UpgradeLog*.XML
259 | UpgradeLog*.htm
260 | ServiceFabricBackup/
261 | *.rptproj.bak
262 |
263 | # SQL Server files
264 | *.mdf
265 | *.ldf
266 | *.ndf
267 |
268 | # Business Intelligence projects
269 | *.rdl.data
270 | *.bim.layout
271 | *.bim_*.settings
272 | *.rptproj.rsuser
273 | *- [Bb]ackup.rdl
274 | *- [Bb]ackup ([0-9]).rdl
275 | *- [Bb]ackup ([0-9][0-9]).rdl
276 |
277 | # Microsoft Fakes
278 | FakesAssemblies/
279 |
280 | # GhostDoc plugin setting file
281 | *.GhostDoc.xml
282 |
283 | # Node.js Tools for Visual Studio
284 | .ntvs_analysis.dat
285 | node_modules/
286 |
287 | # Visual Studio 6 build log
288 | *.plg
289 |
290 | # Visual Studio 6 workspace options file
291 | *.opt
292 |
293 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
294 | *.vbw
295 |
296 | # Visual Studio LightSwitch build output
297 | **/*.HTMLClient/GeneratedArtifacts
298 | **/*.DesktopClient/GeneratedArtifacts
299 | **/*.DesktopClient/ModelManifest.xml
300 | **/*.Server/GeneratedArtifacts
301 | **/*.Server/ModelManifest.xml
302 | _Pvt_Extensions
303 |
304 | # Paket dependency manager
305 | .paket/paket.exe
306 | paket-files/
307 |
308 | # FAKE - F# Make
309 | .fake/
310 |
311 | # CodeRush personal settings
312 | .cr/personal
313 |
314 | # Python Tools for Visual Studio (PTVS)
315 | __pycache__/
316 | *.pyc
317 |
318 | # Cake - Uncomment if you are using it
319 | # tools/**
320 | # !tools/packages.config
321 |
322 | # Tabs Studio
323 | *.tss
324 |
325 | # Telerik's JustMock configuration file
326 | *.jmconfig
327 |
328 | # BizTalk build output
329 | *.btp.cs
330 | *.btm.cs
331 | *.odx.cs
332 | *.xsd.cs
333 |
334 | # OpenCover UI analysis results
335 | OpenCover/
336 |
337 | # Azure Stream Analytics local run output
338 | ASALocalRun/
339 |
340 | # MSBuild Binary and Structured Log
341 | *.binlog
342 |
343 | # NVidia Nsight GPU debugger configuration file
344 | *.nvuser
345 |
346 | # MFractors (Xamarin productivity tool) working folder
347 | .mfractor/
348 |
349 | # Local History for Visual Studio
350 | .localhistory/
351 |
352 | # BeatPulse healthcheck temp database
353 | healthchecksdb
354 |
355 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
356 | MigrationBackup/
357 |
358 | # Ionide (cross platform F# VS Code tools) working folder
359 | .ionide/
360 |
361 | # Fody - auto-generated XML schema
362 | FodyWeavers.xsd
363 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 | Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/BrowserDetectDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 | service-worker-assets.js
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/Pages/Index.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 | Demo for Browser Detect component for Blazor
4 |
5 |
This is the demo website for the Browser Detect component for
6 | Blazor WebAssembly and
7 | Blazor Server with .NET6.
8 | Firstly, if you need help or info about this component, read the
9 | post about this component
10 | and leave your message in the Forum . Now I'm detecting Windows 11
!
11 |
12 |
13 |
16 |
17 |
18 |
19 | @if(Info == null)
20 | {
21 | Loading browser info...
22 | }
23 | else
24 | {
25 |
26 |
27 |
28 |
29 | Property
30 |
31 |
32 | Value
33 |
34 |
35 |
36 |
37 |
38 | Browser Name
39 | @Info.BrowserName
40 |
41 |
42 | Browser Major
43 | @Info.BrowserMajor
44 |
45 |
46 | Browser Version
47 | @Info.BrowserVersion
48 |
49 |
50 | CPU Architect
51 | @(CPUInfoString ?? Info.CPUArchitect)
52 |
53 |
54 | Device Model
55 | @Info.DeviceModel
56 |
57 |
58 | Device Type
59 | @Info.DeviceType
60 |
61 |
62 | Device Vendor
63 | @Info.DeviceVendor
64 |
65 |
66 | Engine Name
67 | @Info.EngineName
68 |
69 |
70 | Engine Version
71 | @Info.EngineVersion
72 |
73 |
74 |
75 | GPU Renderer
76 |
77 | @Info.GPURenderer
78 |
79 |
80 |
81 | GPU Vendor
82 |
83 |
84 | @Info.GPUVendor
85 |
86 |
87 |
88 | Is Desktop?
89 | @Info.IsDesktop
90 |
91 |
92 | Is Mobile?
93 | @Info.IsMobile
94 |
95 |
96 | Is Tablet?
97 | @Info.IsTablet
98 |
99 |
100 | Is Android?
101 | @Info.IsAndroid
102 |
103 |
104 | Is iPhone?
105 | @Info.IsIPhone
106 |
107 |
108 | Is iPad?
109 | @Info.IsIPad
110 |
111 |
112 | Is iPad Pro?
113 | @Info.IsIPadPro
114 |
115 |
116 | Operating System Name
117 | @Info.OSName
118 |
119 |
120 | Operating System Version
121 |
122 | @(OSInfo ?? Info.OSVersion)
123 |
124 |
125 |
126 | Screen Resolution
127 | @Info.ScreenResolution
128 |
129 |
130 | Time Zone
131 | @Info.TimeZone
132 |
133 |
134 | User Agent
135 | @Info.UserAgent
136 |
137 |
138 |
139 | }
140 |
141 |
142 |
143 | @code {
144 | public BrowserInfo? Info { get; set; }
145 | public string? OSInfo { get; set; } = "";
146 | public string? CPUInfoString { get; set; }
147 |
148 | private void OSArchitectureString(string cpu)
149 | {
150 | CPUInfoString = cpu;
151 | }
152 |
153 | private void OSUpdateString(string version)
154 | {
155 | OSInfo = version;
156 | }
157 | }
--------------------------------------------------------------------------------
/BrowserDetectDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using BrowserDetectDemo;
2 | using Microsoft.AspNetCore.Components.Web;
3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
4 |
5 | var builder = WebAssemblyHostBuilder.CreateDefault(args);
6 | builder.RootComponents.Add("#app");
7 | builder.RootComponents.Add("head::after");
8 |
9 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
10 |
11 | await builder.Build().RunAsync();
12 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:11345",
7 | "sslPort": 44315
8 | }
9 | },
10 | "profiles": {
11 | "BrowserDetectDemo": {
12 | "commandName": "Project",
13 | "launchBrowser": true,
14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
15 | "applicationUrl": "https://localhost:7047;http://localhost:5047",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | },
19 | "dotnetRunMessages": true
20 | },
21 | "IIS Express": {
22 | "commandName": "IISExpress",
23 | "launchBrowser": true,
24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/BrowserDetectDemo/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
4 |
7 |
8 |
9 |
15 |
16 |
17 | @Body
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/Shared/MainLayout.razor.css:
--------------------------------------------------------------------------------
1 | .page {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 |
7 | main {
8 | flex: 1;
9 | }
10 |
11 | .sidebar {
12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
13 | }
14 |
15 | .top-row {
16 | background-color: #f7f7f7;
17 | border-bottom: 1px solid #d6d5d5;
18 | justify-content: flex-end;
19 | height: 3.5rem;
20 | display: flex;
21 | align-items: center;
22 | }
23 |
24 | .top-row ::deep a, .top-row ::deep .btn-link {
25 | white-space: nowrap;
26 | margin-left: 1.5rem;
27 | text-decoration: none;
28 | }
29 |
30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
31 | text-decoration: underline;
32 | }
33 |
34 | .top-row ::deep a:first-child {
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | }
38 |
39 | @media (max-width: 640.98px) {
40 | .top-row:not(.auth) {
41 | display: none;
42 | }
43 |
44 | .top-row.auth {
45 | justify-content: space-between;
46 | }
47 |
48 | .top-row ::deep a, .top-row ::deep .btn-link {
49 | margin-left: 0;
50 | }
51 | }
52 |
53 | @media (min-width: 641px) {
54 | .page {
55 | flex-direction: row;
56 | }
57 |
58 | .sidebar {
59 | width: 250px;
60 | height: 100vh;
61 | position: sticky;
62 | top: 0;
63 | }
64 |
65 | .top-row {
66 | position: sticky;
67 | top: 0;
68 | z-index: 1;
69 | }
70 |
71 | .top-row.auth ::deep a:first-child {
72 | flex: 1;
73 | text-align: right;
74 | width: 0;
75 | }
76 |
77 | .top-row, article {
78 | padding-left: 2rem !important;
79 | padding-right: 1.5rem !important;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/Shared/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
9 |
10 |
19 |
20 | @code {
21 | private bool collapseNavMenu = true;
22 |
23 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
24 |
25 | private void ToggleNavMenu()
26 | {
27 | collapseNavMenu = !collapseNavMenu;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/Shared/NavMenu.razor.css:
--------------------------------------------------------------------------------
1 | .navbar-toggler {
2 | background-color: rgba(255, 255, 255, 0.1);
3 | }
4 |
5 | .top-row {
6 | height: 3.5rem;
7 | background-color: rgba(0,0,0,0.4);
8 | }
9 |
10 | .navbar-brand {
11 | font-size: 1.1rem;
12 | }
13 |
14 | .oi {
15 | width: 2rem;
16 | font-size: 1.1rem;
17 | vertical-align: text-top;
18 | top: -2px;
19 | }
20 |
21 | .nav-item {
22 | font-size: 0.9rem;
23 | padding-bottom: 0.5rem;
24 | }
25 |
26 | .nav-item:first-of-type {
27 | padding-top: 1rem;
28 | }
29 |
30 | .nav-item:last-of-type {
31 | padding-bottom: 1rem;
32 | }
33 |
34 | .nav-item ::deep a {
35 | color: #d7d7d7;
36 | border-radius: 4px;
37 | height: 3rem;
38 | display: flex;
39 | align-items: center;
40 | line-height: 3rem;
41 | }
42 |
43 | .nav-item ::deep a.active {
44 | background-color: rgba(255,255,255,0.25);
45 | color: white;
46 | }
47 |
48 | .nav-item ::deep a:hover {
49 | background-color: rgba(255,255,255,0.1);
50 | color: white;
51 | }
52 |
53 | @media (min-width: 641px) {
54 | .navbar-toggler {
55 | display: none;
56 | }
57 |
58 | .collapse {
59 | /* Never collapse the sidebar for wide screens */
60 | display: block;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 | @using BrowserDetectDemo
10 | @using BrowserDetectDemo.Shared
11 |
12 | @using PSC.Blazor.Components.BrowserDetect
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/app.css:
--------------------------------------------------------------------------------
1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
2 |
3 | html, body {
4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
5 | }
6 |
7 | h1:focus {
8 | outline: none;
9 | }
10 |
11 | a, .btn-link {
12 | color: #0071c1;
13 | }
14 |
15 | .btn-primary {
16 | color: #fff;
17 | background-color: #1b6ec2;
18 | border-color: #1861ac;
19 | }
20 |
21 | .content {
22 | padding-top: 1.1rem;
23 | }
24 |
25 | .valid.modified:not([type=checkbox]) {
26 | outline: 1px solid #26b050;
27 | }
28 |
29 | .invalid {
30 | outline: 1px solid red;
31 | }
32 |
33 | .validation-message {
34 | color: red;
35 | }
36 |
37 | #blazor-error-ui {
38 | background: lightyellow;
39 | bottom: 0;
40 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
41 | display: none;
42 | left: 0;
43 | padding: 0.6rem 1.25rem 0.7rem 1.25rem;
44 | position: fixed;
45 | width: 100%;
46 | z-index: 1000;
47 | }
48 |
49 | #blazor-error-ui .dismiss {
50 | cursor: pointer;
51 | position: absolute;
52 | right: 0.75rem;
53 | top: 0.5rem;
54 | }
55 |
56 | .blazor-error-boundary {
57 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
58 | padding: 1rem 1rem 1rem 3.7rem;
59 | color: white;
60 | }
61 |
62 | .blazor-error-boundary::after {
63 | content: "An error has occurred."
64 | }
65 |
66 | table {
67 | width: 100%;
68 | border-collapse: collapse;
69 | }
70 | /* Zebra striping */
71 | tr:nth-of-type(odd) {
72 | background: #eee;
73 | }
74 |
75 | th {
76 | background: #333;
77 | color: white;
78 | font-weight: bold;
79 | }
80 |
81 | td, th {
82 | padding: 6px;
83 | border: 1px solid #ccc;
84 | text-align: left;
85 | }
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/FONT-LICENSE:
--------------------------------------------------------------------------------
1 | SIL OPEN FONT LICENSE Version 1.1
2 |
3 | Copyright (c) 2014 Waybury
4 |
5 | PREAMBLE
6 | The goals of the Open Font License (OFL) are to stimulate worldwide
7 | development of collaborative font projects, to support the font creation
8 | efforts of academic and linguistic communities, and to provide a free and
9 | open framework in which fonts may be shared and improved in partnership
10 | with others.
11 |
12 | The OFL allows the licensed fonts to be used, studied, modified and
13 | redistributed freely as long as they are not sold by themselves. The
14 | fonts, including any derivative works, can be bundled, embedded,
15 | redistributed and/or sold with any software provided that any reserved
16 | names are not used by derivative works. The fonts and derivatives,
17 | however, cannot be released under any other type of license. The
18 | requirement for fonts to remain under this license does not apply
19 | to any document created using the fonts or their derivatives.
20 |
21 | DEFINITIONS
22 | "Font Software" refers to the set of files released by the Copyright
23 | Holder(s) under this license and clearly marked as such. This may
24 | include source files, build scripts and documentation.
25 |
26 | "Reserved Font Name" refers to any names specified as such after the
27 | copyright statement(s).
28 |
29 | "Original Version" refers to the collection of Font Software components as
30 | distributed by the Copyright Holder(s).
31 |
32 | "Modified Version" refers to any derivative made by adding to, deleting,
33 | or substituting -- in part or in whole -- any of the components of the
34 | Original Version, by changing formats or by porting the Font Software to a
35 | new environment.
36 |
37 | "Author" refers to any designer, engineer, programmer, technical
38 | writer or other person who contributed to the Font Software.
39 |
40 | PERMISSION & CONDITIONS
41 | Permission is hereby granted, free of charge, to any person obtaining
42 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
43 | redistribute, and sell modified and unmodified copies of the Font
44 | Software, subject to the following conditions:
45 |
46 | 1) Neither the Font Software nor any of its individual components,
47 | in Original or Modified Versions, may be sold by itself.
48 |
49 | 2) Original or Modified Versions of the Font Software may be bundled,
50 | redistributed and/or sold with any software, provided that each copy
51 | contains the above copyright notice and this license. These can be
52 | included either as stand-alone text files, human-readable headers or
53 | in the appropriate machine-readable metadata fields within text or
54 | binary files as long as those fields can be easily viewed by the user.
55 |
56 | 3) No Modified Version of the Font Software may use the Reserved Font
57 | Name(s) unless explicit written permission is granted by the corresponding
58 | Copyright Holder. This restriction only applies to the primary font name as
59 | presented to the users.
60 |
61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
62 | Software shall not be used to promote, endorse or advertise any
63 | Modified Version, except to acknowledge the contribution(s) of the
64 | Copyright Holder(s) and the Author(s) or with their explicit written
65 | permission.
66 |
67 | 5) The Font Software, modified or unmodified, in part or in whole,
68 | must be distributed entirely under this license, and must not be
69 | distributed under any other license. The requirement for fonts to
70 | remain under this license does not apply to any document created
71 | using the Font Software.
72 |
73 | TERMINATION
74 | This license becomes null and void if any of the above conditions are
75 | not met.
76 |
77 | DISCLAIMER
78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
86 | OTHER DEALINGS IN THE FONT SOFTWARE.
87 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/ICON-LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Waybury
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
13 | all 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
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/README.md:
--------------------------------------------------------------------------------
1 | [Open Iconic v1.1.1](http://useiconic.com/open)
2 | ===========
3 |
4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons)
5 |
6 |
7 |
8 | ## What's in Open Iconic?
9 |
10 | * 223 icons designed to be legible down to 8 pixels
11 | * Super-light SVG files - 61.8 for the entire set
12 | * SVG sprite—the modern replacement for icon fonts
13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats
14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats
15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px.
16 |
17 |
18 | ## Getting Started
19 |
20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections.
21 |
22 | ### General Usage
23 |
24 | #### Using Open Iconic's SVGs
25 |
26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute).
27 |
28 | ```
29 |
30 | ```
31 |
32 | #### Using Open Iconic's SVG Sprite
33 |
34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack.
35 |
36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.*
37 |
38 | ```
39 |
40 |
41 |
42 | ```
43 |
44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions.
45 |
46 | ```
47 | .icon {
48 | width: 16px;
49 | height: 16px;
50 | }
51 | ```
52 |
53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag.
54 |
55 | ```
56 | .icon-account-login {
57 | fill: #f00;
58 | }
59 | ```
60 |
61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/).
62 |
63 | #### Using Open Iconic's Icon Font...
64 |
65 |
66 | ##### …with Bootstrap
67 |
68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}`
69 |
70 |
71 | ```
72 |
73 | ```
74 |
75 |
76 | ```
77 |
78 | ```
79 |
80 | ##### …with Foundation
81 |
82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}`
83 |
84 | ```
85 |
86 | ```
87 |
88 |
89 | ```
90 |
91 | ```
92 |
93 | ##### …on its own
94 |
95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}`
96 |
97 | ```
98 |
99 | ```
100 |
101 | ```
102 |
103 | ```
104 |
105 |
106 | ## License
107 |
108 | ### Icons
109 |
110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT).
111 |
112 | ### Fonts
113 |
114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web).
115 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'}
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.eot
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.otf
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
9 | By P.J. Onori
10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
11 |
12 |
13 |
14 |
27 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
45 |
47 |
49 |
51 |
53 |
55 |
57 |
59 |
61 |
63 |
65 |
67 |
69 |
71 |
74 |
76 |
79 |
81 |
84 |
86 |
88 |
91 |
93 |
95 |
98 |
100 |
102 |
104 |
106 |
109 |
112 |
115 |
117 |
121 |
123 |
125 |
127 |
130 |
132 |
134 |
136 |
138 |
141 |
143 |
145 |
147 |
149 |
151 |
153 |
155 |
157 |
159 |
162 |
165 |
167 |
169 |
172 |
174 |
177 |
179 |
181 |
183 |
185 |
189 |
191 |
194 |
196 |
198 |
200 |
202 |
205 |
207 |
209 |
211 |
213 |
215 |
218 |
220 |
222 |
224 |
226 |
228 |
230 |
232 |
234 |
236 |
238 |
241 |
243 |
245 |
247 |
249 |
251 |
253 |
256 |
259 |
261 |
263 |
265 |
267 |
269 |
272 |
274 |
276 |
280 |
282 |
285 |
287 |
289 |
292 |
295 |
298 |
300 |
302 |
304 |
306 |
309 |
312 |
314 |
316 |
318 |
320 |
322 |
324 |
326 |
330 |
334 |
338 |
340 |
343 |
345 |
347 |
349 |
351 |
353 |
355 |
358 |
360 |
363 |
365 |
367 |
369 |
371 |
373 |
375 |
377 |
379 |
381 |
383 |
386 |
388 |
390 |
392 |
394 |
396 |
399 |
401 |
404 |
406 |
408 |
410 |
412 |
414 |
416 |
419 |
421 |
423 |
425 |
428 |
431 |
435 |
438 |
440 |
442 |
444 |
446 |
448 |
451 |
453 |
455 |
457 |
460 |
462 |
464 |
466 |
468 |
471 |
473 |
477 |
479 |
481 |
483 |
486 |
488 |
490 |
492 |
494 |
496 |
499 |
501 |
504 |
506 |
509 |
512 |
515 |
517 |
520 |
522 |
524 |
526 |
529 |
532 |
534 |
536 |
539 |
542 |
543 |
544 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/css/open-iconic/font/fonts/open-iconic.woff
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/icon-192.png
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/icon-512.png
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/images/psc_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/BrowserDetectDemo/wwwroot/images/psc_logo.png
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Browser Detect Demo for Blazor
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
128 |
129 |
PureSourceCode.com
130 |
131 | is detecting your browser
132 |
133 |
134 |
135 |
136 | An unhandled error has occurred.
137 |
Reload
138 |
🗙
139 |
140 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "UrlShortener.WebApp",
3 | "short_name": "UrlShortener.WebApp",
4 | "start_url": "./",
5 | "display": "standalone",
6 | "background_color": "#ffffff",
7 | "theme_color": "#03173d",
8 | "prefer_related_applications": false,
9 | "icons": [
10 | {
11 | "src": "icon-512.png",
12 | "type": "image/png",
13 | "sizes": "512x512"
14 | },
15 | {
16 | "src": "icon-192.png",
17 | "type": "image/png",
18 | "sizes": "192x192"
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/service-worker.js:
--------------------------------------------------------------------------------
1 | // In development, always fetch from the network and do not enable offline support.
2 | // This is because caching would make development more difficult (changes would not
3 | // be reflected on the first load after each change).
4 | self.addEventListener('fetch', () => { });
5 |
--------------------------------------------------------------------------------
/BrowserDetectDemo/wwwroot/service-worker.published.js:
--------------------------------------------------------------------------------
1 | // Caution! Be sure you understand the caveats before publishing an application with
2 | // offline support. See https://aka.ms/blazor-offline-considerations
3 |
4 | self.importScripts('./service-worker-assets.js');
5 | self.addEventListener('install', event => event.waitUntil(onInstall(event)));
6 | self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
7 | self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
8 |
9 | const cacheNamePrefix = 'offline-cache-';
10 | const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
11 | const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
12 | const offlineAssetsExclude = [ /^service-worker\.js$/ ];
13 |
14 | async function onInstall(event) {
15 | console.info('Service worker: Install');
16 |
17 | // Fetch and cache all matching items from the assets manifest
18 | const assetsRequests = self.assetsManifest.assets
19 | .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
20 | .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
21 | .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
22 | await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
23 | }
24 |
25 | async function onActivate(event) {
26 | console.info('Service worker: Activate');
27 |
28 | // Delete unused caches
29 | const cacheKeys = await caches.keys();
30 | await Promise.all(cacheKeys
31 | .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
32 | .map(key => caches.delete(key)));
33 | }
34 |
35 | async function onFetch(event) {
36 | let cachedResponse = null;
37 | if (event.request.method === 'GET') {
38 | // For all navigation requests, try to serve index.html from cache
39 | // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
40 | const shouldServeIndexHtml = event.request.mode === 'navigate';
41 |
42 | const request = shouldServeIndexHtml ? 'index.html' : event.request;
43 | const cache = await caches.open(cacheName);
44 | cachedResponse = await cache.match(request);
45 | }
46 |
47 | return cachedResponse || fetch(event.request);
48 | }
49 |
--------------------------------------------------------------------------------
/PSC.Blazor.Components.BrowserDetect.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32112.339
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSC.Blazor.Components.BrowserDetect", "src\PSC.Blazor.Components.BrowserDetect.csproj", "{E6A105EE-D6F5-476E-B261-B93C9BF480CA}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrowserDetectDemo", "BrowserDetectDemo\BrowserDetectDemo.csproj", "{07176B1F-8C18-445F-9D57-0B4609C3C685}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Other files", "Other files", "{83B379A2-C3D5-4CB2-AA91-7051AD526D3F}"
11 | ProjectSection(SolutionItems) = preProject
12 | README.md = README.md
13 | EndProjectSection
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {E6A105EE-D6F5-476E-B261-B93C9BF480CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {E6A105EE-D6F5-476E-B261-B93C9BF480CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {E6A105EE-D6F5-476E-B261-B93C9BF480CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {E6A105EE-D6F5-476E-B261-B93C9BF480CA}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {07176B1F-8C18-445F-9D57-0B4609C3C685}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {07176B1F-8C18-445F-9D57-0B4609C3C685}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {07176B1F-8C18-445F-9D57-0B4609C3C685}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {07176B1F-8C18-445F-9D57-0B4609C3C685}.Release|Any CPU.Build.0 = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | GlobalSection(ExtensibilityGlobals) = postSolution
34 | SolutionGuid = {DAC5F2CB-9470-463E-A131-D5F11521901E}
35 | EndGlobalSection
36 | EndGlobal
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Browser Detect for Blazor
2 | This is a browser detect component for [Blazor WebAssembly](https://www.puresourcecode.com/tag/blazor-webassembly/) and [Blazor Server](https://www.puresourcecode.com/tag/blazor-server/) with .NET6.
3 |
4 | > If you need help, info or some device is not detected correctly, leave your message in the [Forum](https://www.puresourcecode.com/forum/browser-detect-for-blazor/).
5 |
6 | Read the full post on [PureSourceCode.com](https://www.puresourcecode.com):
7 | - [Version 1.0.11](https://www.puresourcecode.com/dotnet/blazor/browser-detect-component-for-blazor-2/)
8 | - [First version](https://www.puresourcecode.com/dotnet/blazor/browser-detect-component-for-blazor)
9 |
10 | Now, you can try your component by yourself from the website. [Try it now!](https://browserdetect.puresourcecode.com/)
11 |
12 | 
13 |
14 | ## Installation
15 | Fist, you have to add the component from [NuGet](https://www.nuget.org/packages/PSC.Blazor.Components.BrowserDetect/). Then, open your `_Imports.razor` and add the following:
16 |
17 | ```
18 | @using PSC.Blazor.Components.BrowserDetect
19 | ```
20 |
21 | ## Detected capabilities or properties
22 |
23 | | Property | Value |
24 | | --- | --- |
25 | | BrowserName | Name of the browser |
26 | | BrowserMajor | Major version of the browser |
27 | | BrowserVersion | Version of the browser |
28 | | CPUArchitect | If it is possible, the component detect the CPU architecture of the machine |
29 | | DeviceModel | Device model (if it is possible) |
30 | | DeviceType | Device type (if it is possible) |
31 | | DeviceVendor | Device Vendor (if it is possible) |
32 | | EngineName | Browser engine name |
33 | | EngineVersion | Browser engine version |
34 | | GPURenderer | Type of the GPU renderer |
35 | | GPUVendor | Vendor of the GPU |
36 | | IsDesktop | Detect if the device is a desktop computer |
37 | | IsMobile | Detect if the device is a mobile |
38 | | IsTablet | Detect if the device is a tablet |
39 | | IsAndroid | Detect if the device is an Android device |
40 | | IsIPhone | Detect if the device is an iPhone or iPod |
41 | | IsIPad | Detect if the device is an iPad (any version) |
42 | | IsIPadPro | Detect if the device is an iPad Pro |
43 | | OSName | Detect the operating system |
44 | | OSVersion | Version of the operating system |
45 | | ScreenResolution | Detect the screen resolution |
46 | | TimeZone | Read the time zone |
47 | | UserAgent | The full user agent |
48 |
49 | ### Detecting the operating system and the architecture
50 |
51 | To detect the browser and all the other details from the user, it is not always accurate. For example, if the client has `Windows 11` there is no way to
52 | return the correct version of the operating system. If you have a `MacOS`, again JavaScript can't detect from the `navigator` string any info.
53 | The JavaScript can detect the `OSName`, `Windows` or `MacOS` but not the exact version.
54 |
55 | To detect in a correct way the correct version of the operating system and the architecture of the CPU, the component has to run few tests that take time.
56 |
57 | For this reason I added 2 events in the component:
58 |
59 | - OSArchitectureUpdate
60 | - OSVersionUpdate
61 |
62 | So, if you want to receive the notification when of the correct version of the operating system and the CPU architecture, the component is like this code:
63 |
64 | ```
65 |
68 | ```
69 |
70 | then the functions look like
71 |
72 | ```csharp
73 | public BrowserInfo? Info { get; set; }
74 | public string? OSInfo { get; set; } = "";
75 | public string? OSCPUInfoString { get; set; }
76 |
77 | private void OSArchitectureString(string cpu)
78 | {
79 | OSCPUInfoString = cpu;
80 | }
81 |
82 | private void OSUpdateString(string version)
83 | {
84 | OSInfo = version;
85 | }
86 | ```
87 |
88 | Both events return a simple string with the values. For example:
89 |
90 | - if the operating system is `Windows 11`, the `OSUpdateString` receives the string `11`;
91 | - if the operating system is `Windows 10 1809`, the `OSUpdateString` receives the string `10 (1809)`
92 | - if the CPU is 32 bit, the `OSArchitectureString`, receives the string `x86`
93 |
94 | #### Mac Architecture
95 |
96 | - ARM
97 | - ARM 64
98 |
99 | #### Windows Architecture Values
100 |
101 | - x86_64
102 | - x86
103 | - ARM64
104 | - ARM32
105 |
106 | #### Windows Version
107 |
108 | | Version | platformVersion |
109 | |------------|-----------------------------------|
110 | | Win7/8/8.1 | 7/8/8.1 |
111 | | Win10 1507 | 10 (1507) |
112 | | Win10 1511 | 10 (1511) |
113 | | Win10 1607 | 10 (1607) |
114 | | Win10 1703 | 10 (1703) |
115 | | Win10 1709 | 10 (1709) |
116 | | Win10 1803 | 10 (1803) |
117 | | Win10 1809 | 10 (1809) |
118 | | Win10 1903 | 10 (1903 or 10 1909) |
119 | | Win10 1909 | 10 (1903 or 10 1909) |
120 | | Win10 2004 | 10 (2004 or 20H2 or 21H1 or 21H2) |
121 | | Win10 20H2 | 10 (2004 or 20H2 or 21H1 or 21H2) |
122 | | Win10 21H1 | 10 (2004 or 20H2 or 21H1 or 21H2) |
123 | | Win10 21H2 | 10 (2004 or 20H2 or 21H1 or 21H2) |
124 | | Win11 | 11 |
125 |
126 | ## Screenshot
127 |
128 | ### Windows 11
129 | 
130 |
131 | ### Windows 10
132 | 
133 |
134 | ## iPhone
135 | 
136 |
137 | ## Android mobile
138 | 
139 |
140 | ## Amazon Fire tablet
141 | 
142 |
143 | ## iPad
144 | 
145 |
146 | ## iMac
147 |
148 |
149 |
150 | ---
151 |
152 | ## PureSourceCode.com
153 |
154 | [PureSourceCode.com](https://www.puresourcecode.com/) is my personal blog where I publish posts about technologies and in particular source code and projects in [.NET](https://www.puresourcecode.com/category/dotnet/).
155 |
156 | In the last few months, I created a lot of components for [Blazor WebAssembly](https://www.puresourcecode.com/tag/blazor-webassembly/) and [Blazor Server](https://www.puresourcecode.com/tag/blazor-server/).
157 |
158 | My name is Enrico Rossini and you can contact me via:
159 | - [Personal Twitter](https://twitter.com/erossiniuk)
160 | - [LinkedIn](https://www.linkedin.com/in/rossiniuk)
161 | - [Facebook](https://www.facebook.com/puresourcecode)
162 |
163 | ## Blazor Components
164 |
165 | | Component name | Forum | NuGet | Website | Description |
166 | |---|---|---|---|---|
167 | | [AnchorLink]() | [Forum](https://puresourcecode.com/forum/anchorlink/) |  | | An anchor link is a web link that allows users to leapfrog to a specific point on a website page. It saves them the need to scroll and skim read and makes navigation easier. This component is for [Blazor WebAssembly](https://www.puresourcecode.com/tag/blazor-webassembly/) and [Blazor Server](https://www.puresourcecode.com/tag/blazor-server/) |
168 | | [Autocomplete for Blazor](https://www.puresourcecode.com/dotnet/net-core/autocomplete-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/autocomplete-blazor/) |  | | Simple and flexible autocomplete type-ahead functionality for [Blazor WebAssembly](https://www.puresourcecode.com/tag/blazor-webassembly/) and [Blazor Server](https://www.puresourcecode.com/tag/blazor-server/) |
169 | | [Browser Detect for Blazor](https://www.puresourcecode.com/dotnet/blazor/browser-detect-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/browser-detect-for-blazor/) |  | [Demo](https://browserdetect.puresourcecode.com) | Browser detect for Blazor WebAssembly and Blazor Server |
170 | | [ChartJs for Blazor](https://www.puresourcecode.com/dotnet/blazor/blazor-component-for-chartjs/) | [Forum](https://www.puresourcecode.com/forum/chart-js-for-blazor/) |  | [Demo](https://chartjs.puresourcecode.com/) | Add beautiful graphs based on ChartJs in your Blazor application |
171 | | [Clippy for Blazor](https://www.puresourcecode.com/dotnet/blazor/blazor-component-for-chartjs/) | [Forum](https://www.puresourcecode.com/forum/clippy/) |  | [Demo](https://clippy.puresourcecode.com/) | Do you miss Clippy? Here the implementation for Blazor |
172 | | [CodeSnipper for Blazor](https://www.puresourcecode.com/dotnet/blazor/code-snippet-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/codesnippet-for-blazor/) |  | | Add code snippet in your Blazor pages for 196 programming languages with 243 styles |
173 | | [Copy To Clipboard](https://www.puresourcecode.com/dotnet/blazor/copy-to-clipboard-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/copytoclipboard/) |  | | Add a button to copy text in the clipboard |
174 | | [DataTable for Blazor](https://www.puresourcecode.com/dotnet/net-core/datatable-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/forum/datatables/) |  | [Demo](https://datatable.puresourcecode.com/) | DataTable component for Blazor WebAssembly and Blazor Server |
175 | | [Google Tag Manager]() | [Forum]() |  | [Demo](https://datatable.puresourcecode.com/) | Adds Google Tag Manager to the application and manages communication with GTM JavaScript (data layer). |
176 | | [Icons and flags for Blazor](https://www.puresourcecode.com/forum/icons-and-flags-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/icons-and-flags-for-blazor/) |  | | Library with a lot of SVG icons and SVG flags to use in your Razor pages |
177 | | [ImageSelect for Blazor]() | [Forum](https://puresourcecode.com/forum/imageselect/) |  | | This is a Blazor component to display a dropdown list with images based on ms-Dropdown by Marghoob Suleman. This component is built with NET7 for [Blazor WebAssembly](https://www.puresourcecode.com/tag/blazor-webassembly/) and [Blazor Server](https://www.puresourcecode.com/tag/blazor-server/) |
178 | | [Markdown editor for Blazor](https://www.puresourcecode.com/dotnet/blazor/markdown-editor-with-blazor/) | [Forum](https://www.puresourcecode.com/forum/forum/markdown-editor-for-blazor/) |  | [Demo](https://markdown.puresourcecode.com/) | This is a Markdown Editor for use in Blazor. It contains a live preview as well as an embeded help guide for users. |
179 | | [Modal dialog for Blazor](https://www.puresourcecode.com/dotnet/blazor/modal-dialog-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/forum/modal-dialog-for-blazor/) |  | | Simple Modal Dialog for Blazor WebAssembly |
180 | | [Modal windows for Blazor](https://www.puresourcecode.com/dotnet/blazor/modal-dialog-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/forum/modal-dialog-for-blazor/) |  | | Modal Windows for Blazor WebAssembly |
181 | | [Quill for Blazor](https://www.puresourcecode.com/dotnet/blazor/create-a-blazor-component-for-quill/) | [Forum](https://www.puresourcecode.com/forum/forum/quill-for-blazor/) |  | | Quill Component is a custom reusable control that allows us to easily consume Quill and place multiple instances of it on a single page in our Blazor application |
182 | | [ScrollTabs](https://www.puresourcecode.com/dotnet/blazor/scrolltabs-component-for-blazor/) | |  | | Tabs with nice scroll (no scrollbar) and responsive |
183 | | [Segment for Blazor](https://www.puresourcecode.com/dotnet/blazor/segment-control-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/forum/segments-for-blazor/) |  | | This is a Segment component for Blazor Web Assembly and Blazor Server |
184 | | [Tabs for Blazor](https://www.puresourcecode.com/dotnet/blazor/tabs-control-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/forum/tabs-for-blazor/) |  | | This is a Tabs component for Blazor Web Assembly and Blazor Server |
185 | | [Timeline for Blazor](https://www.puresourcecode.com/dotnet/blazor/timeline-component-for-blazor/) | [Forum](https://www.puresourcecode.com/forum/timeline/) |  | | This is a new responsive timeline for Blazor Web Assembly and Blazor Server |
186 | | [Toast for Blazor](https://www.puresourcecode.com/forum/psc-components-and-source-code/) | [Forum](https://www.puresourcecode.com/forum/psc-components-and-source-code/) |  | | Toast notification for Blazor applications |
187 | | [Tours for Blazor](https://www.puresourcecode.com/forum/psc-components-and-source-code/) | [Forum](https://www.puresourcecode.com/forum/psc-components-and-source-code/) |  | | Guide your users in your Blazor applications |
188 | | [TreeView for Blazor]() | [Forum](https://puresourcecode.com/forum/treeview/) |  | | This component is a native Blazor TreeView component for [Blazor WebAssembly](https://www.puresourcecode.com/tag/blazor-webassembly/) and [Blazor Server](https://www.puresourcecode.com/tag/blazor-server/). The component is built with .NET7. |
189 | | [WorldMap for Blazor](https://puresourcecode.com/dotnet/blazor/world-map-component-for-blazor) | [Forum](https://www.puresourcecode.com/forum/worldmap-for-blazor/) |  | [Demo](https://worldmap.puresourcecode.com/) | Show world maps with your data |
190 |
191 | ## C# libraries for .NET6
192 |
193 | | Component name | Forum | NuGet | Description |
194 | |---|---|---|---|
195 | | [PSC.Evaluator](https://www.puresourcecode.com/forum/psc-components-and-source-code/) | [Forum](https://www.puresourcecode.com/forum/forum/psc-extensions/) |  | PSC.Evaluator is a mathematical expressions evaluator library written in C#. Allows to evaluate mathematical, boolean, string and datetime expressions. |
196 | | [PSC.Extensions](https://www.puresourcecode.com/dotnet/net-core/a-lot-of-functions-for-net5/) | [Forum](https://www.puresourcecode.com/forum/forum/psc-extensions/) |  | A lot of functions for .NET5 in a NuGet package that you can download for free. We collected in this package functions for everyday work to help you with claim, strings, enums, date and time, expressions... |
197 |
198 | ## More examples and documentation
199 |
200 | ### Blazor
201 | * [Write a reusable Blazor component](https://www.puresourcecode.com/dotnet/blazor/write-a-reusable-blazor-component/)
202 | * [Getting Started With C# And Blazor](https://www.puresourcecode.com/dotnet/net-core/getting-started-with-c-and-blazor/)
203 | * [Setting Up A Blazor WebAssembly Application](https://www.puresourcecode.com/dotnet/blazor/setting-up-a-blazor-webassembly-application/)
204 | * [Working With Blazor Component Model](https://www.puresourcecode.com/dotnet/blazor/working-with-blazors-component-model/)
205 | * [Secure Blazor WebAssembly With IdentityServer4](https://www.puresourcecode.com/dotnet/blazor/secure-blazor-webassembly-with-identityserver4/)
206 | * [Blazor Using HttpClient With Authentication](https://www.puresourcecode.com/dotnet/blazor/blazor-using-httpclient-with-authentication/)
207 | * [InputSelect component for enumerations in Blazor](https://www.puresourcecode.com/dotnet/blazor/inputselect-component-for-enumerations-in-blazor/)
208 | * [Use LocalStorage with Blazor WebAssembly](https://www.puresourcecode.com/dotnet/blazor/use-localstorage-with-blazor-webassembly/)
209 | * [Modal Dialog component for Blazor](https://www.puresourcecode.com/dotnet/blazor/modal-dialog-component-for-blazor/)
210 | * [Create Tooltip component for Blazor](https://www.puresourcecode.com/dotnet/blazor/create-tooltip-component-for-blazor/)
211 | * [Consume ASP.NET Core Razor components from Razor class libraries | Microsoft Docs](https://docs.microsoft.com/en-us/aspnet/core/blazor/components/class-libraries?view=aspnetcore-5.0&tabs=visual-studio)
212 | * [ChartJs component for Blazor](https://www.puresourcecode.com/dotnet/blazor/blazor-component-for-chartjs/)
213 | * [Labels and OnClickChart for ChartJs](https://www.puresourcecode.com/dotnet/blazor/labels-and-onclickchart-for-chartjs/)
214 |
215 | ### Blazor & NET8
216 | * [Custom User Management with NET8 and Blazor (1st part)](https://puresourcecode.com/dotnet/blazor/custom-user-management-with-net8-and-blazor/)
217 | * [NET8, Blazor and Custom User Management (2nd part)](https://puresourcecode.com/dotnet/blazor/net8-blazor-and-custom-user-management/)
218 |
--------------------------------------------------------------------------------
/src/BrowserDetect.razor:
--------------------------------------------------------------------------------
1 | @inject IJSRuntime JSRuntime
2 |
3 | @code {
4 | #region Inject and JavaScript
5 |
6 | ///
7 | /// The dotnet object reference
8 | ///
9 | private DotNetObjectReference? dotNetObjectRef;
10 |
11 | #endregion
12 | #region Parameters
13 | [Parameter]
14 | public BrowserInfo? browserInfo { get; set; }
15 |
16 | [Parameter]
17 | public EventCallback BrowserInfoChanged { get; set; }
18 |
19 | [Parameter]
20 | public EventCallback OSVersionUpdate { get; set; }
21 |
22 | [Parameter]
23 | public EventCallback OSArchitectureUpdate { get; set; }
24 | #endregion
25 |
26 | protected BrowserDetectJsInterop? JSModule { get; private set; }
27 |
28 | protected override async Task OnAfterRenderAsync(bool firstRender)
29 | {
30 | if (firstRender)
31 | {
32 | JSModule = new BrowserDetectJsInterop(JSRuntime);
33 |
34 | dotNetObjectRef ??= DotNetObjectReference.Create(this);
35 |
36 | browserInfo = await JSModule.BrowserInfo(dotNetObjectRef);
37 | await BrowserInfoChanged.InvokeAsync(browserInfo);
38 | }
39 | }
40 |
41 | [JSInvokable]
42 | public async Task OSUpdateString(string version)
43 | {
44 | await OSVersionUpdate.InvokeAsync(version);
45 | await InvokeAsync(StateHasChanged);
46 | }
47 |
48 | [JSInvokable]
49 | public async Task OSArchitectureUpdateString(string cpu)
50 | {
51 | await OSArchitectureUpdate.InvokeAsync(cpu);
52 | await InvokeAsync(StateHasChanged);
53 | }
54 | }
--------------------------------------------------------------------------------
/src/BrowserDetectJsInterop.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace PSC.Blazor.Components.BrowserDetect
4 | {
5 | // This class provides an example of how JavaScript functionality can be wrapped
6 | // in a .NET class for easy consumption. The associated JavaScript module is
7 | // loaded on demand when first needed.
8 | //
9 | // This class can be registered as scoped DI service and then injected into Blazor
10 | // components for use.
11 |
12 | public class BrowserDetectJsInterop : IAsyncDisposable
13 | {
14 | private readonly Lazy> moduleTask;
15 |
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// The js runtime.
20 | public BrowserDetectJsInterop(IJSRuntime jsRuntime)
21 | {
22 | moduleTask = new(() => jsRuntime.InvokeAsync("import",
23 | "./_content/PSC.Blazor.Components.BrowserDetect/js/browserDetect.js").AsTask());
24 | }
25 |
26 | ///
27 | /// Browsers the information.
28 | ///
29 | /// The dot net object reference.
30 | /// BrowserInfo.
31 | public async ValueTask BrowserInfo(DotNetObjectReference dotNetObjectRef)
32 | {
33 | var module = await moduleTask.Value;
34 | return await module.InvokeAsync("browserDetect", dotNetObjectRef);
35 | }
36 |
37 | ///
38 | /// Dispose as an asynchronous operation.
39 | ///
40 | /// A Task<ValueTask> representing the asynchronous operation.
41 | public async ValueTask DisposeAsync()
42 | {
43 | if (moduleTask.IsValueCreated)
44 | {
45 | var module = await moduleTask.Value;
46 | await module.DisposeAsync();
47 | }
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/Models/BrowserDetect.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PSC.Blazor.Components.BrowserDetect
8 | {
9 | ///
10 | /// Class BrowserInfo.
11 | ///
12 | public class BrowserInfo
13 | {
14 | ///
15 | /// Gets or sets the browser major.
16 | ///
17 | /// The browser major.
18 | public string? BrowserMajor { get; set; }
19 | ///
20 | /// Gets or sets the name of the browser.
21 | ///
22 | /// The name of the browser.
23 | public string? BrowserName { get; set; }
24 | ///
25 | /// Gets or sets the browser version.
26 | ///
27 | /// The browser version.
28 | public string? BrowserVersion { get; set; }
29 | ///
30 | /// Gets or sets the cpu architect.
31 | ///
32 | /// The cpu architect.
33 | public string? CPUArchitect { get; set; }
34 | ///
35 | /// Gets or sets the device model.
36 | ///
37 | /// The device model.
38 | public string? DeviceModel { get; set; }
39 | ///
40 | /// Gets or sets the type of the device.
41 | ///
42 | /// The type of the device.
43 | public string? DeviceType { get; set; }
44 | ///
45 | /// Gets or sets the device vendor.
46 | ///
47 | /// The device vendor.
48 | public string? DeviceVendor { get; set; }
49 | ///
50 | /// Gets or sets the name of the engine.
51 | ///
52 | /// The name of the engine.
53 | public string? EngineName { get; set; }
54 | ///
55 | /// Gets or sets the engine version.
56 | ///
57 | /// The engine version.
58 | public string? EngineVersion { get; set; }
59 | ///
60 | /// Gets or sets the gpu renderer.
61 | ///
62 | /// The gpu renderer.
63 | public string? GPURenderer { get; set; }
64 | ///
65 | /// Gets or sets the gpu vendor.
66 | ///
67 | /// The gpu vendor.
68 | public string? GPUVendor { get; set; }
69 | ///
70 | /// Gets or sets the ip.
71 | ///
72 | /// The ip.
73 | public string? IP { get; set; }
74 | ///
75 | /// Gets or sets a value indicating whether this instance is android.
76 | ///
77 | /// null if [is android] contains no value, true if [is android]; otherwise, false .
78 | public bool? IsAndroid { get; set; }
79 | ///
80 | /// Gets or sets a value indicating whether this instance is desktop.
81 | ///
82 | /// null if [is desktop] contains no value, true if [is desktop]; otherwise, false .
83 | public bool? IsDesktop { get; set; }
84 | ///
85 | /// Gets or sets a value indicating whether this instance is i pad.
86 | ///
87 | /// null if [is i pad] contains no value, true if [is i pad]; otherwise, false .
88 | public bool? IsIPad { get; set; }
89 | ///
90 | /// Gets or sets a value indicating whether this instance is i pad pro.
91 | ///
92 | /// null if [is i pad pro] contains no value, true if [is i pad pro]; otherwise, false .
93 | public bool? IsIPadPro { get; set; }
94 | ///
95 | /// Gets or sets a value indicating whether this instance is i phone.
96 | ///
97 | /// null if [is i phone] contains no value, true if [is i phone]; otherwise, false .
98 | public bool? IsIPhone { get; set; }
99 | ///
100 | /// Gets or sets a value indicating whether this instance is mobile.
101 | ///
102 | /// null if [is mobile] contains no value, true if [is mobile]; otherwise, false .
103 | public bool? IsMobile { get; set; }
104 | ///
105 | /// Gets or sets a value indicating whether this instance is tablet.
106 | ///
107 | /// null if [is tablet] contains no value, true if [is tablet]; otherwise, false .
108 | public bool? IsTablet { get; set; }
109 | ///
110 | /// Gets or sets the name of the os.
111 | ///
112 | /// The name of the os.
113 | public string? OSName { get; set; }
114 | ///
115 | /// Gets or sets the os version.
116 | ///
117 | /// The os version.
118 | public string? OSVersion { get; set; }
119 | ///
120 | /// Gets or sets the screen resolution.
121 | ///
122 | /// The screen resolution.
123 | public string? ScreenResolution { get; set; }
124 | ///
125 | /// Gets or sets the time zone.
126 | ///
127 | /// The time zone.
128 | public string? TimeZone { get; set; }
129 | ///
130 | /// Gets or sets the user agent.
131 | ///
132 | /// The user agent.
133 | public string? UserAgent { get; set; }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/PSC.Blazor.Components.BrowserDetect.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 | psc_logo.ico
8 | True
9 | Browser Detect for Blazor
10 | psc_logo.png
11 | README.md
12 | git
13 | Enrico Rossini - PureSourceCode.com
14 | https://www.puresourcecode.com/dotnet/blazor/browser-detect-component-for-blazor-2/
15 | https://github.com/erossini/BlazorBrowserDetect
16 | blazor, blazor-webassembly, blazor-server, browser, detect,
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | True
26 | \
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | True
42 | \
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Components.Web
2 | @using Microsoft.JSInterop
--------------------------------------------------------------------------------
/src/psc_logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/src/psc_logo.ico
--------------------------------------------------------------------------------
/src/psc_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erossini/BlazorBrowserDetect/517dd91f890a6503061ccdfff252382a0b63c405/src/psc_logo.png
--------------------------------------------------------------------------------
/src/wwwroot/js/browserDetect.js:
--------------------------------------------------------------------------------
1 | export function browserDetect(dotNetObjectRef) {
2 | var parser = new UAParser();
3 | var result = parser.getResult();
4 |
5 | var gl = getGlRenderer();
6 |
7 | var isMobile = navigator.userAgent.toLowerCase().indexOf('mobile') > -1 ?? (isiOS()),
8 | isTablet = navigator.userAgent.toLowerCase().indexOf('tablet') > -1 ?? (isiPadOS() || isiPadPro()),
9 | isAndroid = navigator.userAgent.toLowerCase().indexOf('android') > -1,
10 | isiPhone = isiOS(),
11 | isiPad = isiPadOS() || isiPadPro();
12 |
13 | var agent = window.navigator.userAgent;
14 | var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
15 |
16 | var deviceVendor = result.device.vendor ?? '';
17 | if (deviceVendor === '' && (isiPad || isiPhone || isiPadPro()))
18 | deviceVendor = 'Apple Inc.';
19 |
20 | var deviceModel = result.device.model ?? '';
21 | var deviceType = result.device.type ?? '';
22 |
23 | console.log('Device model ->' + deviceModel);
24 | if (deviceModel === 'iPad' || isiPad) {
25 | deviceType = 'iPad';
26 | deviceModel = getModels().toString();
27 | }
28 | else if (deviceModel === 'iPhone' || isiPhone) {
29 | deviceType = 'iPhone';
30 | deviceModel = getModels().toString();
31 | }
32 |
33 | var osName = parser.getOS().name;
34 | var osVersion = parser.getOS().version;
35 |
36 | var winVer = '';
37 |
38 | if (typeof navigator.userAgentData !== 'undefined') {
39 | navigator.userAgentData.getHighEntropyValues(["platformVersion"])
40 | .then(ua => {
41 | if (navigator.userAgentData.platform === "Windows") {
42 | var majorPlatformVersion = parseInt(ua.platformVersion.split('.')[0]);
43 | if (majorPlatformVersion >= 13) {
44 | winVer = "11 or later";
45 | }
46 | else {
47 | switch (majorPlatformVersion) {
48 | case 0:
49 | winVer = "7/8/8.1";
50 | break;
51 | case 1:
52 | winVer = "10 (1507)";
53 | break;
54 | case 2:
55 | winVer = "10 (1511)";
56 | break;
57 | case 3:
58 | winVer = "10 (1607)";
59 | break;
60 | case 4:
61 | winVer = "10 (1703)";
62 | break;
63 | case 5:
64 | winVer = "10 (1709)";
65 | break;
66 | case 6:
67 | winVer = "10 (1803)";
68 | break;
69 | case 7:
70 | winVer = "10 (1809)";
71 | break;
72 | case 8:
73 | winVer = "10 (1903 or 10 1909)";
74 | break;
75 | case 10:
76 | winVer = "10 (2004 or 20H2 or 21H1 or 21H2)";
77 | break;
78 | default:
79 | break;
80 | }
81 | }
82 |
83 | osVersion = winVer;
84 |
85 | dotNetObjectRef.invokeMethodAsync('OSUpdateString', winVer).then(null, function (err) {
86 | throw new Error(err);
87 | });
88 | }
89 | else if (navigator.userAgentData.platform === "macOS") {
90 | var macVer = ua.platformVersion;
91 | dotNetObjectRef.invokeMethodAsync('OSUpdateString', macVer).then(null, function (err) {
92 | throw new Error(err);
93 | });
94 | }
95 | });
96 |
97 | navigator.userAgentData.getHighEntropyValues(["architecture", "bitness"])
98 | .then(ua => {
99 | if (navigator.userAgentData.platform === "Windows") {
100 | var winCPU = '';
101 | if (ua.architecture === 'x86') {
102 | if (ua.bitness === '64') {
103 | winCPU = "x86_64";
104 | }
105 | else if (ua.bitness === '32') {
106 | winCPU = "x86";
107 | }
108 | }
109 | else if (ua.architecture === 'arm') {
110 | if (ua.bitness === '64') {
111 | winCPU = "ARM64";
112 | }
113 | else if (ua.bitness === '32') {
114 | winCPU = "ARM32";
115 | }
116 | }
117 |
118 | dotNetObjectRef.invokeMethodAsync('OSArchitectureUpdateString', winCPU).then(null, function (err) {
119 | throw new Error(err);
120 | });
121 | }
122 | else if (navigator.userAgentData.platform === "macOS") {
123 | var macCPU = ua.architecture + ' ' + ua.bitness;
124 | dotNetObjectRef.invokeMethodAsync('OSArchitectureUpdateString', macCPU).then(null, function (err) {
125 | throw new Error(err);
126 | });
127 | }
128 | });
129 | }
130 |
131 | var rtn = {
132 | BrowserMajor: result.browser.major ?? '',
133 | BrowserName: result.browser.name ?? '',
134 | BrowserVersion: result.browser.version ?? '',
135 | CPUArchitect: result.cpu.architecture ?? '',
136 | DeviceModel: deviceModel,
137 | DeviceType: deviceType,
138 | DeviceVendor: deviceVendor,
139 | EngineName: result.engine.name ?? '',
140 | EngineVersion: result.engine.version ?? '',
141 | GPURenderer: gl.glRenderer,
142 | GPUVendor: gl.glVendor,
143 | IsDesktop: isDesktop(),
144 | IsMobile: isMobile ?? false,
145 | IsTablet: isTablet ?? false,
146 | IsAndroid: isAndroid ?? false,
147 | IsIPhone: isiPhone ?? false,
148 | IsIPad: isiPad ?? false,
149 | isIpadPro: isiPadPro(),
150 | OSName: osName,
151 | OSVersion: osVersion,
152 | ScreenResolution: (window.screen.width * window.devicePixelRatio) + 'x' + (window.screen.height * window.devicePixelRatio),
153 | TimeZone: timeZone,
154 | UserAgent: agent
155 | };
156 |
157 | return rtn;
158 | }
159 |
160 | function isiOS() {
161 | if (/iPhone|iPod/.test(navigator.platform)) {
162 | return true;
163 | } else {
164 | return false;
165 | }
166 | }
167 |
168 | function isiPadOS() {
169 | if (/iPad/.test(navigator.platform)) {
170 | return true;
171 | } else {
172 | return false;
173 | }
174 | }
175 |
176 | function isiPadPro() {
177 | var ratio = window.devicePixelRatio || 1;
178 | var screen = {
179 | width: window.screen.width * ratio,
180 | height: window.screen.height * ratio
181 | };
182 |
183 | return (screen.width === 2048 && screen.height === 2732) ||
184 | (screen.width === 2732 && screen.height === 2048) ||
185 | (screen.width === 1536 && screen.height === 2048) ||
186 | (screen.width === 2048 && screen.height === 1536);
187 | }
188 |
189 | function isDesktop() {
190 | if ((navigator.userAgent.match(/iPhone/i)) ||
191 | (navigator.userAgent.match(/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|vodafone|o2|pocket|kindle|mobile|pda|psp|treo)/i)) ||
192 | (navigator.userAgent.match(/iPod/i)) ||
193 | (navigator.userAgent.match(/operamini/i)) ||
194 | (navigator.userAgent.match(/blackberry/i)) ||
195 | (navigator.userAgent.match(/(palmos|palm|hiptop|avantgo|plucker|xiino|blazer|elaine)/i)) ||
196 | (navigator.userAgent.match(/(windowsce; ppc;|windows ce;smartphone;|windows ce; iemobile) /i)) ||
197 | (navigator.userAgent.match(/android/i)) || isiOS() || isiPadOS() || isiPadPro()) {
198 | return false;
199 | }
200 | else {
201 | return true;
202 | }
203 | }
204 |
205 | function OSVersion() {
206 | var rtn = "";
207 |
208 | var userAgent = window.navigator.userAgent;
209 |
210 | if (userAgent.indexOf("Windows NT 10.0") > 0) {
211 | rtn = "Windows 10/11";
212 | }
213 | else if (userAgent.indexOf("Windows NT 6.3") > 0) {
214 | rtn = "Windows 8.1";
215 | }
216 | else if (userAgent.indexOf("Windows NT 6.2") > 0) {
217 | rtn = "Windows 8";
218 | }
219 | else if (userAgent.indexOf("Windows NT 6.1") > 0) {
220 | rtn = "Windows 7";
221 | }
222 | else if (userAgent.indexOf("Windows NT 6.0") > 0) {
223 | rtn = "Windows Vista";
224 | }
225 | else if (userAgent.indexOf("Windows NT 5.2") > 0) {
226 | rtn = "Windows Server 2003; Windows XP x64 Edition";
227 | }
228 | else if (userAgent.indexOf("Windows NT 5.1") > 0) {
229 | rtn = "Windows XP";
230 | }
231 | else if (userAgent.indexOf("Windows NT 5.01") > 0) {
232 | rtn = "Windows 2000, Service Pack 1 (SP1)";
233 | }
234 | else if (userAgent.indexOf("Windows NT 5.0") > 0) {
235 | rtn = "Windows 2000";
236 | }
237 | else if (userAgent.indexOf("Windows NT 4.0") > 0) {
238 | rtn = "Microsoft Windows NT 4.0";
239 | }
240 | else if (userAgent.indexOf("Win 9x 4.90") > 0) {
241 | rtn = "Windows Millennium Edition (Windows Me)";
242 | }
243 | else if (userAgent.indexOf("Windows 98") > 0) {
244 | rtn = "Windows 98";
245 | }
246 | else if (userAgent.indexOf("Windows 95") > 0) {
247 | rtn = "Windows 95";
248 | }
249 | else if (userAgent.indexOf("Windows CE") > 0) {
250 | rtn = "Windows CE";
251 | }
252 | else if (userAgent.indexOf("iPhone OS") > 0) {
253 | rtn = "iPhone OS";
254 | }
255 | else if (userAgent.indexOf("Mac OS") > 0) {
256 | rtn = "Max OS";
257 | }
258 | else if (userAgent.indexOf("Android") > 0) {
259 | rtn = "Android";
260 | }
261 | else if (userAgent.indexOf("Silk") > 0) {
262 | rtn = "Amazon Fire";
263 | }
264 | else if (userAgent.indexOf("facebook") > 0) {
265 | rtn = "Facebook External Hit";
266 | }
267 | else if (userAgent.indexOf("Twitterbot") > 0) {
268 | rtn = "Twitterbot";
269 | }
270 | else if (userAgent.indexOf("WhatsApp") > 0) {
271 | rtn = "WhatsApp";
272 | }
273 | else {
274 | //Others
275 | }
276 |
277 | return rtn;
278 | }
279 |
280 | var canvas, gl, glRenderer, models,
281 | devices = [
282 | ['a7', '640x1136', ['iPhone 5', 'iPhone 5s']],
283 | ['a7', '1536x2048', ['iPad Air', 'iPad Mini 2', 'iPad Mini 3', 'iPad Pro 9.7']],
284 | ['a8', '640x1136', ['iPod touch (6th gen)']],
285 | ['a8', '750x1334', ['iPhone 6']],
286 | ['a8', '1242x2208', ['iPhone 6 Plus']],
287 | ['a8', '1536x2048', ['iPad Air 2', 'iPad Mini 4']],
288 | ['a9', '640x1136', ['iPhone SE']],
289 | ['a9', '750x1334', ['iPhone 6s']],
290 | ['a9', '1242x2208', ['iPhone 6s Plus']],
291 | ['a9x', '1536x2048', ['iPad Pro (1st gen 9.7-inch)']],
292 | ['a9x', '2048x2732', ['iPad Pro (1st gen 12.9-inch)']],
293 | ['a10', '750x1334', ['iPhone 7']],
294 | ['a10', '1242x2208', ['iPhone 7 Plus']],
295 | ['a10x', '1668x2224', ['iPad Pro (2th gen 10.5-inch)']],
296 | ['a10x', '2048x2732', ['iPad Pro (2th gen 12.9-inch)']],
297 | ['a11', '750x1334', ['iPhone 8']],
298 | ['a11', '1242x2208', ['iPhone 8 Plus']],
299 | ['a11', '1125x2436', ['iPhone X']],
300 | ['a12', '828x1792', ['iPhone Xr']],
301 | ['a12', '1125x2436', ['iPhone Xs']],
302 | ['a12', '1242x2688', ['iPhone Xs Max']],
303 | ['a12x', '1668x2388', ['iPad Pro (3rd gen 11-inch)']],
304 | ['a12x', '2048x2732', ['iPad Pro (3rd gen 12.9-inch)']],
305 | ['a15', '2556x1179', ['iPhone 14']],
306 | ['a15', '2796x1290', ['iPhone 14 Max']]
307 | ];
308 |
309 | function getCanvas() {
310 | if (canvas == null) {
311 | canvas = document.createElement('canvas');
312 | }
313 |
314 | return canvas;
315 | }
316 |
317 | function getGl() {
318 | if (gl == null) {
319 | gl = getCanvas().getContext('webgl');
320 | }
321 |
322 | return gl;
323 | }
324 |
325 | function getResolution() {
326 | var ratio = window.devicePixelRatio || 1;
327 | return (Math.min(screen.width, screen.height) * ratio)
328 | + 'x' + (Math.max(screen.width, screen.height) * ratio);
329 | }
330 |
331 | function getGlRenderer() {
332 | var glVendor = 'Unknown';
333 |
334 | if (glRenderer == null) {
335 | const gl = document.createElement("canvas").getContext("webgl");
336 | // try to get the extensions
337 | const ext = gl.getExtension("WEBGL_debug_renderer_info");
338 |
339 | // if the extension exists, find out the info.
340 | if (ext) {
341 | glRenderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
342 | glVendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
343 | }
344 | else {
345 | glRenderer = "Unknown";
346 | }
347 | }
348 |
349 | return { glRenderer, glVendor };
350 | }
351 |
352 | function getModels() {
353 | if (models === undefined) {
354 | var gpu = getGlRenderer();
355 | var matches = gpu.glRenderer.toLowerCase().includes('apple');
356 | var res = getResolution();
357 |
358 | models = ['unknown'];
359 |
360 | if (matches) {
361 | for (var i = 0; i < devices.length; i++) {
362 | var device = devices[i];
363 |
364 | var res2 = res.split('x').reverse().join('x');
365 | if (res == device[1] || res2 == device[1]) {
366 | models = device[2];
367 | break;
368 | }
369 | }
370 | }
371 | }
372 |
373 | return models;
374 | }
375 |
376 | function getDeviceInfo() {
377 | if (window.MobileDevice == undefined) {
378 | window.MobileDevice = {};
379 | }
380 |
381 | window.MobileDevice.getGlRenderer = getGlRenderer;
382 | window.MobileDevice.getModels = getModels;
383 | window.MobileDevice.getResolution = getResolution;
384 |
385 | var currentModels = getModels();
386 | //var match = match.toLowerCase().replace(/\s+$/, '') + ' ';
387 |
388 | for (var i = 0; i < currentModels.length; i++) {
389 | var model = currentModels[i].toLowerCase() + ' ';
390 |
391 | if (0 === model.indexOf(math)) {
392 | return true;
393 | }
394 | }
395 | }
396 |
397 | (function (window, undefined) {
398 | 'use strict';
399 |
400 | //////////////
401 | // Constants
402 | /////////////
403 |
404 | var LIBVERSION = '0.7.31',
405 | EMPTY = '',
406 | UNKNOWN = '?',
407 | FUNC_TYPE = 'function',
408 | UNDEF_TYPE = 'undefined',
409 | OBJ_TYPE = 'object',
410 | STR_TYPE = 'string',
411 | MAJOR = 'major',
412 | MODEL = 'model',
413 | NAME = 'name',
414 | TYPE = 'type',
415 | VENDOR = 'vendor',
416 | VERSION = 'version',
417 | ARCHITECTURE = 'architecture',
418 | CONSOLE = 'console',
419 | MOBILE = 'mobile',
420 | TABLET = 'tablet',
421 | SMARTTV = 'smarttv',
422 | WEARABLE = 'wearable',
423 | EMBEDDED = 'embedded',
424 | UA_MAX_LENGTH = 275;
425 |
426 | var AMAZON = 'Amazon',
427 | APPLE = 'Apple',
428 | ASUS = 'ASUS',
429 | BLACKBERRY = 'BlackBerry',
430 | BROWSER = 'Browser',
431 | CHROME = 'Chrome',
432 | EDGE = 'Edge',
433 | FIREFOX = 'Firefox',
434 | GOOGLE = 'Google',
435 | HUAWEI = 'Huawei',
436 | LG = 'LG',
437 | MICROSOFT = 'Microsoft',
438 | MOTOROLA = 'Motorola',
439 | OPERA = 'Opera',
440 | SAMSUNG = 'Samsung',
441 | SONY = 'Sony',
442 | XIAOMI = 'Xiaomi',
443 | ZEBRA = 'Zebra',
444 | FACEBOOK = 'Facebook';
445 |
446 | ///////////
447 | // Helper
448 | //////////
449 |
450 | var extend = function (regexes, extensions) {
451 | var mergedRegexes = {};
452 | for (var i in regexes) {
453 | if (extensions[i] && extensions[i].length % 2 === 0) {
454 | mergedRegexes[i] = extensions[i].concat(regexes[i]);
455 | } else {
456 | mergedRegexes[i] = regexes[i];
457 | }
458 | }
459 | return mergedRegexes;
460 | },
461 | enumerize = function (arr) {
462 | var enums = {};
463 | for (var i = 0; i < arr.length; i++) {
464 | enums[arr[i].toUpperCase()] = arr[i];
465 | }
466 | return enums;
467 | },
468 | has = function (str1, str2) {
469 | return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
470 | },
471 | lowerize = function (str) {
472 | return str.toLowerCase();
473 | },
474 | majorize = function (version) {
475 | return typeof (version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined;
476 | },
477 | trim = function (str, len) {
478 | if (typeof (str) === STR_TYPE) {
479 | str = str.replace(/^\s\s*/, EMPTY).replace(/\s\s*$/, EMPTY);
480 | return typeof (len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
481 | }
482 | };
483 |
484 | ///////////////
485 | // Map helper
486 | //////////////
487 |
488 | var rgxMapper = function (ua, arrays) {
489 |
490 | var i = 0, j, k, p, q, matches, match;
491 |
492 | // loop through all regexes maps
493 | while (i < arrays.length && !matches) {
494 |
495 | var regex = arrays[i], // even sequence (0,2,4,..)
496 | props = arrays[i + 1]; // odd sequence (1,3,5,..)
497 | j = k = 0;
498 |
499 | // try matching uastring with regexes
500 | while (j < regex.length && !matches) {
501 |
502 | matches = regex[j++].exec(ua);
503 |
504 | if (!!matches) {
505 | for (p = 0; p < props.length; p++) {
506 | match = matches[++k];
507 | q = props[p];
508 | // check if given property is actually array
509 | if (typeof q === OBJ_TYPE && q.length > 0) {
510 | if (q.length === 2) {
511 | if (typeof q[1] == FUNC_TYPE) {
512 | // assign modified match
513 | this[q[0]] = q[1].call(this, match);
514 | } else {
515 | // assign given value, ignore regex match
516 | this[q[0]] = q[1];
517 | }
518 | } else if (q.length === 3) {
519 | // check whether function or regex
520 | if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
521 | // call function (usually string mapper)
522 | this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
523 | } else {
524 | // sanitize match using given regex
525 | this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
526 | }
527 | } else if (q.length === 4) {
528 | this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
529 | }
530 | } else {
531 | this[q] = match ? match : undefined;
532 | }
533 | }
534 | }
535 | }
536 | i += 2;
537 | }
538 | },
539 |
540 | strMapper = function (str, map) {
541 |
542 | for (var i in map) {
543 | // check if current value is array
544 | if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
545 | for (var j = 0; j < map[i].length; j++) {
546 | if (has(map[i][j], str)) {
547 | return (i === UNKNOWN) ? undefined : i;
548 | }
549 | }
550 | } else if (has(map[i], str)) {
551 | return (i === UNKNOWN) ? undefined : i;
552 | }
553 | }
554 | return str;
555 | };
556 |
557 | ///////////////
558 | // String map
559 | //////////////
560 |
561 | // Safari < 3.0
562 | var oldSafariMap = {
563 | '1.0': '/8',
564 | '1.2': '/1',
565 | '1.3': '/3',
566 | '2.0': '/412',
567 | '2.0.2': '/416',
568 | '2.0.3': '/417',
569 | '2.0.4': '/419',
570 | '?': '/'
571 | },
572 | windowsVersionMap = {
573 | 'ME': '4.90',
574 | 'NT 3.11': 'NT3.51',
575 | 'NT 4.0': 'NT4.0',
576 | '2000': 'NT 5.0',
577 | 'XP': ['NT 5.1', 'NT 5.2'],
578 | 'Vista': 'NT 6.0',
579 | '7': 'NT 6.1',
580 | '8': 'NT 6.2',
581 | '8.1': 'NT 6.3',
582 | '10': ['NT 6.4', 'NT 10.0'],
583 | 'RT': 'ARM'
584 | };
585 |
586 | //////////////
587 | // Regex map
588 | /////////////
589 |
590 | var regexes = {
591 |
592 | browser: [[
593 |
594 | /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
595 | ], [VERSION, [NAME, 'Chrome']], [
596 | /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
597 | ], [VERSION, [NAME, 'Edge']], [
598 |
599 | // Presto based
600 | /(opera mini)\/([-\w\.]+)/i, // Opera Mini
601 | /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet
602 | /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera
603 | ], [NAME, VERSION], [
604 | /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0
605 | ], [VERSION, [NAME, OPERA + ' Mini']], [
606 | /\bopr\/([\w\.]+)/i // Opera Webkit
607 | ], [VERSION, [NAME, OPERA]], [
608 |
609 | // Mixed
610 | /(kindle)\/([\w\.]+)/i, // Kindle
611 | /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
612 | // Trident based
613 | /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
614 | /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser
615 | /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
616 |
617 | // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
618 | /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale|qqbrowserlite|qq)\/([-\w\.]+)/i,
619 | // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ
620 | /(weibo)__([\d\.]+)/i // Weibo
621 | ], [NAME, VERSION], [
622 | /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
623 | ], [VERSION, [NAME, 'UC' + BROWSER]], [
624 | /\bqbcore\/([\w\.]+)/i // WeChat Desktop for Windows Built-in Browser
625 | ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [
626 | /micromessenger\/([\w\.]+)/i // WeChat
627 | ], [VERSION, [NAME, 'WeChat']], [
628 | /konqueror\/([\w\.]+)/i // Konqueror
629 | ], [VERSION, [NAME, 'Konqueror']], [
630 | /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11
631 | ], [VERSION, [NAME, 'IE']], [
632 | /yabrowser\/([\w\.]+)/i // Yandex
633 | ], [VERSION, [NAME, 'Yandex']], [
634 | /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
635 | ], [[NAME, /(.+)/, '$1 Secure ' + BROWSER], VERSION], [
636 | /\bfocus\/([\w\.]+)/i // Firefox Focus
637 | ], [VERSION, [NAME, FIREFOX + ' Focus']], [
638 | /\bopt\/([\w\.]+)/i // Opera Touch
639 | ], [VERSION, [NAME, OPERA + ' Touch']], [
640 | /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser
641 | ], [VERSION, [NAME, 'Coc Coc']], [
642 | /dolfin\/([\w\.]+)/i // Dolphin
643 | ], [VERSION, [NAME, 'Dolphin']], [
644 | /coast\/([\w\.]+)/i // Opera Coast
645 | ], [VERSION, [NAME, OPERA + ' Coast']], [
646 | /miuibrowser\/([\w\.]+)/i // MIUI Browser
647 | ], [VERSION, [NAME, 'MIUI ' + BROWSER]], [
648 | /fxios\/([-\w\.]+)/i // Firefox for iOS
649 | ], [VERSION, [NAME, FIREFOX]], [
650 | /\bqihu|(qi?ho?o?|360)browser/i // 360
651 | ], [[NAME, '360 ' + BROWSER]], [
652 | /(oculus|samsung|sailfish)browser\/([\w\.]+)/i
653 | ], [[NAME, /(.+)/, '$1 ' + BROWSER], VERSION], [ // Oculus/Samsung/Sailfish Browser
654 | /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
655 | ], [[NAME, /_/g, ' '], VERSION], [
656 | /(electron)\/([\w\.]+) safari/i, // Electron-based App
657 | /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
658 | /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser
659 | ], [NAME, VERSION], [
660 | /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser
661 | /(lbbrowser)/i // LieBao Browser
662 | ], [NAME], [
663 |
664 | // WebView
665 | /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android
666 | ], [[NAME, FACEBOOK], VERSION], [
667 | /safari (line)\/([\w\.]+)/i, // Line App for iOS
668 | /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
669 | /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
670 | ], [NAME, VERSION], [
671 | /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
672 | ], [VERSION, [NAME, 'GSA']], [
673 |
674 | /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
675 | ], [VERSION, [NAME, CHROME + ' Headless']], [
676 |
677 | / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
678 | ], [[NAME, CHROME + ' WebView'], VERSION], [
679 |
680 | /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
681 | ], [VERSION, [NAME, 'Android ' + BROWSER]], [
682 |
683 | /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia
684 | ], [NAME, VERSION], [
685 |
686 | /version\/([\w\.]+) .*mobile\/\w+ (safari)/i // Mobile Safari
687 | ], [VERSION, [NAME, 'Mobile Safari']], [
688 | /version\/([\w\.]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile
689 | ], [VERSION, NAME], [
690 | /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
691 | ], [NAME, [VERSION, strMapper, oldSafariMap]], [
692 |
693 | /(webkit|khtml)\/([\w\.]+)/i
694 | ], [NAME, VERSION], [
695 |
696 | // Gecko based
697 | /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
698 | ], [[NAME, 'Netscape'], VERSION], [
699 | /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
700 | ], [VERSION, [NAME, FIREFOX + ' Reality']], [
701 | /ekiohf.+(flow)\/([\w\.]+)/i, // Flow
702 | /(swiftfox)/i, // Swiftfox
703 | /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i,
704 | // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar
705 | /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
706 | // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
707 | /(firefox)\/([\w\.]+)/i, // Other Firefox-based
708 | /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
709 |
710 | // Other
711 | /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
712 | // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser
713 | /(links) \(([\w\.]+)/i // Links
714 | ], [NAME, VERSION]
715 | ],
716 |
717 | cpu: [[
718 |
719 | /(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i // AMD64 (x64)
720 | ], [[ARCHITECTURE, 'amd64']], [
721 |
722 | /(ia32(?=;))/i // IA32 (quicktime)
723 | ], [[ARCHITECTURE, lowerize]], [
724 |
725 | /((?:i[346]|x)86)[;\)]/i // IA32 (x86)
726 | ], [[ARCHITECTURE, 'ia32']], [
727 |
728 | /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
729 | ], [[ARCHITECTURE, 'arm64']], [
730 |
731 | /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
732 | ], [[ARCHITECTURE, 'armhf']], [
733 |
734 | // PocketPC mistakenly identified as PowerPC
735 | /windows (ce|mobile); ppc;/i
736 | ], [[ARCHITECTURE, 'arm']], [
737 |
738 | /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC
739 | ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
740 |
741 | /(sun4\w)[;\)]/i // SPARC
742 | ], [[ARCHITECTURE, 'sparc']], [
743 |
744 | /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i
745 | // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
746 | ], [[ARCHITECTURE, lowerize]]
747 | ],
748 |
749 | device: [[
750 |
751 | //////////////////////////
752 | // MOBILES & TABLETS
753 | // Ordered by popularity
754 | /////////////////////////
755 |
756 | // Samsung
757 | /\b(sch-i[89]0\d|shw-m380s|sm-[pt]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
758 | ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [
759 | /\b((?:s[cgp]h|gt|sm)-\w+|galaxy nexus)/i,
760 | /samsung[- ]([-\w]+)/i,
761 | /sec-(sgh\w+)/i
762 | ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
763 |
764 | // Apple
765 | /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone
766 | ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
767 | /\((ipad);[-\w\),; ]+apple/i, // iPad
768 | /applecoremedia\/[\w\.]+ \((ipad)/i,
769 | /\b(ipad)\d\d?,\d\d?[;\]].+ios/i
770 | ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
771 |
772 | // Huawei
773 | /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i
774 | ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
775 | /(?:huawei|honor)([-\w ]+)[;\)]/i,
776 | /\b(nexus 6p|\w{2,4}-[atu]?[ln][01259x][012359][an]?)\b(?!.+d\/s)/i
777 | ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
778 |
779 | // Xiaomi
780 | /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
781 | /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
782 | /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
783 | /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
784 | /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi
785 | ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
786 | /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets
787 | ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
788 |
789 | // OPPO
790 | /; (\w+) bui.+ oppo/i,
791 | /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
792 | ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [
793 |
794 | // Vivo
795 | /vivo (\w+)(?: bui|\))/i,
796 | /\b(v[12]\d{3}\w?[at])(?: bui|;)/i
797 | ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [
798 |
799 | // Realme
800 | /\b(rmx[12]\d{3})(?: bui|;|\))/i
801 | ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
802 |
803 | // Motorola
804 | /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
805 | /\bmot(?:orola)?[- ](\w*)/i,
806 | /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
807 | ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [
808 | /\b(mz60\d|xoom[2 ]{0,2}) build\//i
809 | ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
810 |
811 | // LG
812 | /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
813 | ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
814 | /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
815 | /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i,
816 | /\blg-?([\d\w]+) bui/i
817 | ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [
818 |
819 | // Lenovo
820 | /(ideatab[-\w ]+)/i,
821 | /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i
822 | ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
823 |
824 | // Nokia
825 | /(?:maemo|nokia).*(n900|lumia \d+)/i,
826 | /nokia[-_ ]?([-\w\.]*)/i
827 | ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [
828 |
829 | // Google
830 | /(pixel c)\b/i // Google Pixel C
831 | ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [
832 | /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel
833 | ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [
834 |
835 | // Sony
836 | /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
837 | ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [
838 | /sony tablet [ps]/i,
839 | /\b(?:sony)?sgp\w+(?: bui|\))/i
840 | ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [
841 |
842 | // OnePlus
843 | / (kb2005|in20[12]5|be20[12][59])\b/i,
844 | /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
845 | ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [
846 |
847 | // Amazon
848 | /(alexa)webm/i,
849 | /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk
850 | /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD
851 | ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [
852 | /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone
853 | ], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [
854 |
855 | // BlackBerry
856 | /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
857 | ], [MODEL, VENDOR, [TYPE, TABLET]], [
858 | /\b((?:bb[a-f]|st[hv])100-\d)/i,
859 | /\(bb10; (\w+)/i // BlackBerry 10
860 | ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
861 |
862 | // Asus
863 | /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i
864 | ], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [
865 | / (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i
866 | ], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [
867 |
868 | // HTC
869 | /(nexus 9)/i // HTC Nexus 9
870 | ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [
871 | /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC
872 |
873 | // ZTE
874 | /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
875 | /(alcatel|geeksphone|nexian|panasonic|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
876 | ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
877 |
878 | // Acer
879 | /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i
880 | ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [
881 |
882 | // Meizu
883 | /droid.+; (m[1-5] note) bui/i,
884 | /\bmz-([-\w]{2,})/i
885 | ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
886 |
887 | // Sharp
888 | /\b(sh-?[altvz]?\d\d[a-ekm]?)/i
889 | ], [MODEL, [VENDOR, 'Sharp'], [TYPE, MOBILE]], [
890 |
891 | // MIXED
892 | /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
893 | // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
894 | /(hp) ([\w ]+\w)/i, // HP iPAQ
895 | /(asus)-?(\w+)/i, // Asus
896 | /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
897 | /(lenovo)[-_ ]?([-\w]+)/i, // Lenovo
898 | /(jolla)/i, // Jolla
899 | /(oppo) ?([\w ]+) bui/i // OPPO
900 | ], [VENDOR, MODEL, [TYPE, MOBILE]], [
901 |
902 | /(archos) (gamepad2?)/i, // Archos
903 | /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
904 | /(kindle)\/([\w\.]+)/i, // Kindle
905 | /(nook)[\w ]+build\/(\w+)/i, // Nook
906 | /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
907 | /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
908 | /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
909 | /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
910 | /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
911 | ], [VENDOR, MODEL, [TYPE, TABLET]], [
912 |
913 | /(surface duo)/i // Surface Duo
914 | ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [
915 | /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone
916 | ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [
917 | /(u304aa)/i // AT&T
918 | ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
919 | /\bsie-(\w*)/i // Siemens
920 | ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
921 | /\b(rct\w+) b/i // RCA Tablets
922 | ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
923 | /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
924 | ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
925 | /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
926 | ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
927 | /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
928 | ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
929 | /\b(tm\d{3}\w+) b/i
930 | ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
931 | /\b(k88) b/i // ZTE K Series Tablet
932 | ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
933 | /\b(nx\d{3}j) b/i // ZTE Nubia
934 | ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
935 | /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
936 | ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
937 | /\b(zur\d{3}) b/i // Swiss ZUR Tablet
938 | ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
939 | /\b((zeki)?tb.*\b) b/i // Zeki Tablets
940 | ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
941 | /\b([yr]\d{2}) b/i,
942 | /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
943 | ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
944 | /\b(ns-?\w{0,9}) b/i // Insignia Tablets
945 | ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
946 | /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
947 | ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
948 | /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
949 | ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
950 | /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
951 | ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
952 | /\b(ph-1) /i // Essential PH-1
953 | ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
954 | /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
955 | ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
956 | /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
957 | ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
958 | /\btu_(1491) b/i // Rotor Tablets
959 | ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
960 | /(shield[\w ]+) b/i // Nvidia Shield Tablets
961 | ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [
962 | /(sprint) (\w+)/i // Sprint Phones
963 | ], [VENDOR, MODEL, [TYPE, MOBILE]], [
964 | /(kin\.[onetw]{3})/i // Microsoft Kin
965 | ], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [
966 | /droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra
967 | ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [
968 | /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
969 | ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [
970 |
971 | ///////////////////
972 | // CONSOLES
973 | ///////////////////
974 |
975 | /(ouya)/i, // Ouya
976 | /(nintendo) ([wids3utch]+)/i // Nintendo
977 | ], [VENDOR, MODEL, [TYPE, CONSOLE]], [
978 | /droid.+; (shield) bui/i // Nvidia
979 | ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [
980 | /(playstation [345portablevi]+)/i // Playstation
981 | ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [
982 | /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox
983 | ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
984 |
985 | ///////////////////
986 | // SMARTTVS
987 | ///////////////////
988 |
989 | /smart-tv.+(samsung)/i // Samsung
990 | ], [VENDOR, [TYPE, SMARTTV]], [
991 | /hbbtv.+maple;(\d+)/i
992 | ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
993 | /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
994 | ], [[VENDOR, LG], [TYPE, SMARTTV]], [
995 | /(apple) ?tv/i // Apple TV
996 | ], [VENDOR, [MODEL, APPLE + ' TV'], [TYPE, SMARTTV]], [
997 | /crkey/i // Google Chromecast
998 | ], [[MODEL, CHROME + 'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
999 | /droid.+aft(\w)( bui|\))/i // Fire TV
1000 | ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
1001 | /\(dtv[\);].+(aquos)/i // Sharp
1002 | ], [MODEL, [VENDOR, 'Sharp'], [TYPE, SMARTTV]], [
1003 | /(bravia[\w- ]+) bui/i // Sony
1004 | ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
1005 | /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
1006 | /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices
1007 | ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
1008 | /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
1009 | ], [[TYPE, SMARTTV]], [
1010 |
1011 | ///////////////////
1012 | // WEARABLES
1013 | ///////////////////
1014 |
1015 | /((pebble))app/i // Pebble
1016 | ], [VENDOR, MODEL, [TYPE, WEARABLE]], [
1017 | /droid.+; (glass) \d/i // Google Glass
1018 | ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [
1019 | /droid.+; (wt63?0{2,3})\)/i
1020 | ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [
1021 | /(quest( 2)?)/i // Oculus Quest
1022 | ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [
1023 |
1024 | ///////////////////
1025 | // EMBEDDED
1026 | ///////////////////
1027 |
1028 | /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla
1029 | ], [VENDOR, [TYPE, EMBEDDED]], [
1030 |
1031 | ////////////////////
1032 | // MIXED (GENERIC)
1033 | ///////////////////
1034 |
1035 | /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors
1036 | ], [MODEL, [TYPE, MOBILE]], [
1037 | /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors
1038 | ], [MODEL, [TYPE, TABLET]], [
1039 | /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
1040 | ], [[TYPE, TABLET]], [
1041 | /(phone|mobile(?:[;\/]| safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile
1042 | ], [[TYPE, MOBILE]], [
1043 | /(android[-\w\. ]{0,9});.+buil/i // Generic Android Device
1044 | ], [MODEL, [VENDOR, 'Generic']]
1045 | ],
1046 |
1047 | engine: [[
1048 |
1049 | /windows.+ edge\/([\w\.]+)/i // EdgeHTML
1050 | ], [VERSION, [NAME, EDGE + 'HTML']], [
1051 |
1052 | /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink
1053 | ], [VERSION, [NAME, 'Blink']], [
1054 |
1055 | /(presto)\/([\w\.]+)/i, // Presto
1056 | /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
1057 | /ekioh(flow)\/([\w\.]+)/i, // Flow
1058 | /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
1059 | /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab
1060 | ], [NAME, VERSION], [
1061 |
1062 | /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko
1063 | ], [VERSION, NAME]
1064 | ],
1065 |
1066 | os: [[
1067 |
1068 | // Windows
1069 | /microsoft (windows) (vista|xp)/i // Windows (iTunes)
1070 | ], [NAME, VERSION], [
1071 | /(windows) nt 6\.2; (arm)/i, // Windows RT
1072 | /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone
1073 | /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i
1074 | ], [NAME, [VERSION, strMapper, windowsVersionMap]], [
1075 | /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i
1076 | ], [[NAME, 'Windows'], [VERSION, strMapper, windowsVersionMap]], [
1077 |
1078 | // iOS/macOS
1079 | /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
1080 | /cfnetwork\/.+darwin/i
1081 | ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
1082 | /(mac os x) ?([\w\. ]*)/i,
1083 | /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS
1084 | ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [
1085 |
1086 | // Mobile OSes
1087 | /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86
1088 | ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS
1089 | /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i,
1090 | /(blackberry)\w*\/([\w\.]*)/i, // Blackberry
1091 | /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS
1092 | /\((series40);/i // Series 40
1093 | ], [NAME, VERSION], [
1094 | /\(bb(10);/i // BlackBerry 10
1095 | ], [VERSION, [NAME, BLACKBERRY]], [
1096 | /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian
1097 | ], [VERSION, [NAME, 'Symbian']], [
1098 | /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
1099 | ], [VERSION, [NAME, FIREFOX + ' OS']], [
1100 | /web0s;.+rt(tv)/i,
1101 | /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS
1102 | ], [VERSION, [NAME, 'webOS']], [
1103 |
1104 | // Google Chromecast
1105 | /crkey\/([\d\.]+)/i // Google Chromecast
1106 | ], [VERSION, [NAME, CHROME + 'cast']], [
1107 | /(cros) [\w]+ ([\w\.]+\w)/i // Chromium OS
1108 | ], [[NAME, 'Chromium OS'], VERSION], [
1109 |
1110 | // Console
1111 | /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation
1112 | /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
1113 |
1114 | // Other
1115 | /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
1116 | /(mint)[\/\(\) ]?(\w*)/i, // Mint
1117 | /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux
1118 | /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
1119 | // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire
1120 | /(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux
1121 | /(gnu) ?([\w\.]*)/i, // GNU
1122 | /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly
1123 | /(haiku) (\w+)/i // Haiku
1124 | ], [NAME, VERSION], [
1125 | /(sunos) ?([\w\.\d]*)/i // Solaris
1126 | ], [[NAME, 'Solaris'], VERSION], [
1127 | /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
1128 | /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
1129 | /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX
1130 | /(unix) ?([\w\.]*)/i // UNIX
1131 | ], [NAME, VERSION]
1132 | ]
1133 | };
1134 |
1135 | /////////////////
1136 | // Constructor
1137 | ////////////////
1138 |
1139 | var UAParser = function (ua, extensions) {
1140 |
1141 | if (typeof ua === OBJ_TYPE) {
1142 | extensions = ua;
1143 | ua = undefined;
1144 | }
1145 |
1146 | if (!(this instanceof UAParser)) {
1147 | return new UAParser(ua, extensions).getResult();
1148 | }
1149 |
1150 | var _ua = ua || ((typeof window !== UNDEF_TYPE && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
1151 | var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
1152 |
1153 | this.getBrowser = function () {
1154 | var _browser = {};
1155 | _browser[NAME] = undefined;
1156 | _browser[VERSION] = undefined;
1157 | rgxMapper.call(_browser, _ua, _rgxmap.browser);
1158 | _browser.major = majorize(_browser.version);
1159 | return _browser;
1160 | };
1161 | this.getCPU = function () {
1162 | var _cpu = {};
1163 | _cpu[ARCHITECTURE] = undefined;
1164 | rgxMapper.call(_cpu, _ua, _rgxmap.cpu);
1165 | return _cpu;
1166 | };
1167 | this.getDevice = function () {
1168 | var _device = {};
1169 | _device[VENDOR] = undefined;
1170 | _device[MODEL] = undefined;
1171 | _device[TYPE] = undefined;
1172 | rgxMapper.call(_device, _ua, _rgxmap.device);
1173 | return _device;
1174 | };
1175 | this.getEngine = function () {
1176 | var _engine = {};
1177 | _engine[NAME] = undefined;
1178 | _engine[VERSION] = undefined;
1179 | rgxMapper.call(_engine, _ua, _rgxmap.engine);
1180 | return _engine;
1181 | };
1182 | this.getOS = function () {
1183 | var _os = {};
1184 | _os[NAME] = undefined;
1185 | _os[VERSION] = undefined;
1186 | rgxMapper.call(_os, _ua, _rgxmap.os);
1187 | return _os;
1188 | };
1189 | this.getResult = function () {
1190 | return {
1191 | ua: this.getUA(),
1192 | browser: this.getBrowser(),
1193 | engine: this.getEngine(),
1194 | os: this.getOS(),
1195 | device: this.getDevice(),
1196 | cpu: this.getCPU()
1197 | };
1198 | };
1199 | this.getUA = function () {
1200 | return _ua;
1201 | };
1202 | this.setUA = function (ua) {
1203 | _ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
1204 | return this;
1205 | };
1206 | this.setUA(_ua);
1207 | return this;
1208 | };
1209 |
1210 | UAParser.VERSION = LIBVERSION;
1211 | UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]);
1212 | UAParser.CPU = enumerize([ARCHITECTURE]);
1213 | UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
1214 | UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);
1215 |
1216 | ///////////
1217 | // Export
1218 | //////////
1219 |
1220 | // check js environment
1221 | if (typeof (exports) !== UNDEF_TYPE) {
1222 | // nodejs env
1223 | if (typeof module !== UNDEF_TYPE && module.exports) {
1224 | exports = module.exports = UAParser;
1225 | }
1226 | exports.UAParser = UAParser;
1227 | } else {
1228 | // requirejs env (optional)
1229 | if (typeof (define) === FUNC_TYPE && define.amd) {
1230 | define(function () {
1231 | return UAParser;
1232 | });
1233 | } else if (typeof window !== UNDEF_TYPE) {
1234 | // browser env
1235 | window.UAParser = UAParser;
1236 | }
1237 | }
1238 |
1239 | // jQuery/Zepto specific (optional)
1240 | // Note:
1241 | // In AMD env the global scope should be kept clean, but jQuery is an exception.
1242 | // jQuery always exports to global scope, unless jQuery.noConflict(true) is used,
1243 | // and we should catch that.
1244 | var $ = typeof window !== UNDEF_TYPE && (window.jQuery || window.Zepto);
1245 | if ($ && !$.ua) {
1246 | var parser = new UAParser();
1247 | $.ua = parser.getResult();
1248 | $.ua.get = function () {
1249 | return parser.getUA();
1250 | };
1251 | $.ua.set = function (ua) {
1252 | parser.setUA(ua);
1253 | var result = parser.getResult();
1254 | for (var prop in result) {
1255 | $.ua[prop] = result[prop];
1256 | }
1257 | };
1258 | }
1259 |
1260 | })(typeof window === 'object' ? window : this);
--------------------------------------------------------------------------------