diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d97e430
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+# Files and directories created by pub
+.packages
+.pub/
+build/
+coverage/
+packages
+pubspec.lock
+
+# Directory created by dartdoc
+doc/api/
+
+# JetBrains IDEs
+.idea/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9493cb6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2015 Workiva Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/README.md b/README.md
index eb70c30..74b71d1 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,21 @@
# platform_detect
+
+A library for detecting browser and platform type and version.
+
+## Usage
+
+A simple usage example:
+
+```dart
+import 'package:platform_detect/platform_detect.dart';
+
+main() {
+ if (browser.isChrome) {
+ print('thank you for being a friend');
+ }
+
+ if (operatingSystem.isMac) {
+ print('');
+ }
+}
+```
diff --git a/example/index.html b/example/index.html
new file mode 100644
index 0000000..1fab873
--- /dev/null
+++ b/example/index.html
@@ -0,0 +1,58 @@
+
+
+
+ Platform Detect example
+
+
+
+
+
Current platform
+
Your Browser:
+
+
+
+
+
+
Is Chrome
+
Is Firefox
+
Is Internet Explorer
+
Is Safari
+
+
+
+ Version:
+
+
Your Operating System:
+
+
Test other values
+
+
+
vendor
+
+
+
+
appVersion
+
+
+
+
appName
+
+
+
+
userAgent
+
+
+
+
+
Evaluates to:
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/main.dart b/example/main.dart
new file mode 100644
index 0000000..8712337
--- /dev/null
+++ b/example/main.dart
@@ -0,0 +1,53 @@
+import 'dart:html';
+import 'package:platform_detect/src/navigator.dart';
+import 'package:platform_detect/src/browser.dart';
+import 'package:platform_detect/src/operating_system.dart';
+import 'package:platform_detect/platform_detect.dart';
+
+main() {
+ _parseCurrentBrowser();
+ ButtonElement evaluate = querySelector('#evaluate-test');
+ evaluate.onClick.listen((_) => _parseTestValues());
+}
+
+void _parseCurrentBrowser() {
+ document.querySelector('#current-browser').text = browser.name;
+ document.querySelector('#current-vendor').text = window.navigator.vendor;
+ document.querySelector('#current-appVersion').text = window.navigator.appVersion;
+ document.querySelector('#current-appName').text = window.navigator.appName;
+ document.querySelector('#current-userAgent').text = window.navigator.userAgent;
+
+ CheckboxInputElement isChrome = document.querySelector('#current-is-chrome');
+ isChrome.checked = browser.isChrome;
+
+ CheckboxInputElement isFirefox = document.querySelector('#current-is-firefox');
+ isFirefox.checked = browser.isFirefox;
+
+ CheckboxInputElement isSafari = document.querySelector('#current-is-safari');
+ isSafari.checked = browser.isSafari;
+
+ CheckboxInputElement isInternetExplorer = document.querySelector('#current-is-ie');
+ isInternetExplorer.checked = browser.isInternetExplorer;
+
+ document.querySelector('#current-version').text = browser.version.toString();
+
+ document.querySelector('#current-os').text = operatingSystem.name;
+}
+
+void _parseTestValues() {
+ InputElement testVendor = querySelector('#test-vendor');
+ InputElement testAppVersion = querySelector('#test-appVersion');
+ InputElement testAppName = querySelector('#test-appName');
+ InputElement testUserAgent = querySelector('#test-userAgent');
+ var navigator = new TestNavigator();
+ navigator.vendor = testVendor.value.trim();
+ navigator.appVersion = testAppVersion.value.trim();
+ navigator.appName = testAppName.value.trim();
+ navigator.userAgent = testUserAgent.value.trim();
+ Browser.navigator = navigator;
+ OperatingSystem.navigator = navigator;
+
+ querySelector('#test-browser-name').text = browser.name;
+ querySelector('#test-browser-version').text = browser.version.toString();
+ querySelector('#test-os-name').text = operatingSystem.name;
+}
\ No newline at end of file
diff --git a/lib/platform_detect.dart b/lib/platform_detect.dart
new file mode 100644
index 0000000..f600a60
--- /dev/null
+++ b/lib/platform_detect.dart
@@ -0,0 +1,41 @@
+/// Enables detection of browser type and version and operating system
+///
+/// Use browser.isChrome or operatingSystem.isMac
+library platform_detect;
+
+import 'dart:html';
+
+import 'package:platform_detect/src/browser.dart';
+import 'package:platform_detect/src/navigator.dart';
+import 'package:platform_detect/src/operating_system.dart';
+
+Browser _browser;
+
+/// Current browser info
+Browser get browser {
+ if (_browser == null) {
+ Browser.navigator = new _HtmlNavigator();
+ _browser = Browser.getCurrentBrowser();
+ }
+
+ return _browser;
+}
+
+OperatingSystem _operatingSystem;
+
+/// Current operating system info
+OperatingSystem get operatingSystem {
+ if (_operatingSystem == null) {
+ OperatingSystem.navigator = new _HtmlNavigator();
+ _operatingSystem = OperatingSystem.getCurrentOperatingSystem();
+ }
+
+ return _operatingSystem;
+}
+
+class _HtmlNavigator implements NavigatorProvider {
+ String get vendor => window.navigator.vendor;
+ String get appVersion => window.navigator.appVersion;
+ String get appName => window.navigator.appName;
+ String get userAgent => window.navigator.userAgent;
+}
diff --git a/lib/src/browser.dart b/lib/src/browser.dart
new file mode 100644
index 0000000..1cf7304
--- /dev/null
+++ b/lib/src/browser.dart
@@ -0,0 +1,146 @@
+library platform_detect.browser;
+
+import 'package:pub_semver/pub_semver.dart';
+import 'package:platform_detect/src/navigator.dart';
+
+/// Matches a browser name with how it is represented in window.navigator
+class Browser {
+ static NavigatorProvider navigator;
+
+ static Browser getCurrentBrowser() {
+ return _knownBrowsers.firstWhere(
+ (browser) => browser._matchesNavigator(navigator),
+ orElse: () => UnknownBrowser);
+ }
+
+ static Browser UnknownBrowser = new Browser('Unknown', null, null);
+
+ Browser(this.name, bool matchesNavigator(NavigatorProvider navigator),
+ Version parseVersion(NavigatorProvider navigator))
+ : this._matchesNavigator = matchesNavigator,
+ this._parseVersion = parseVersion;
+
+ final String name;
+ final Function _matchesNavigator;
+ final Function _parseVersion;
+
+ Version _version;
+
+ Version get version {
+ if (_version == null) {
+ if (_parseVersion != null) {
+ _version = _parseVersion(Browser.navigator);
+ } else {
+ _version = new Version(0, 0, 0);
+ }
+ }
+
+ return _version;
+ }
+
+ static List _knownBrowsers = [
+ _chrome,
+ _firefox,
+ _safari,
+ _internetExplorer
+ ];
+
+ bool get isChrome => this == _chrome;
+ bool get isFirefox => this == _firefox;
+ bool get isSafari => this == _safari;
+ bool get isInternetExplorer => this == _internetExplorer;
+}
+
+Browser _chrome = new _Chrome();
+Browser _firefox = new _Firefox();
+Browser _safari = new _Safari();
+Browser _internetExplorer = new _InternetExplorer();
+
+class _Chrome extends Browser {
+ _Chrome() : super('Chrome', _isChrome, _getVersion);
+
+ static bool _isChrome(NavigatorProvider navigator) {
+ var vendor = navigator.vendor;
+ return vendor != null && vendor.contains('Google');
+ }
+
+ static Version _getVersion(NavigatorProvider navigator) {
+ Match match = new RegExp(r"Chrome/(\d+)\.(\d+)\.(\d+)\.(\d+)\s")
+ .firstMatch(navigator.appVersion);
+ var major = int.parse(match.group(1));
+ var minor = int.parse(match.group(2));
+ var patch = int.parse(match.group(3));
+ var build = match.group(4);
+ return new Version(major, minor, patch, build: build);
+ }
+}
+
+class _Firefox extends Browser {
+ _Firefox() : super('Firefox', _isFirefox, _getVersion);
+
+ static bool _isFirefox(NavigatorProvider navigator) {
+ return navigator.userAgent.contains('Firefox');
+ }
+
+ static Version _getVersion(NavigatorProvider navigator) {
+ Match match =
+ new RegExp(r'rv:(\d+)\.(\d+)\)').firstMatch(navigator.userAgent);
+ var major = int.parse(match.group(1));
+ var minor = int.parse(match.group(2));
+ return new Version(major, minor, 0);
+ }
+}
+
+class _Safari extends Browser {
+ _Safari() : super('Safari', _isSafari, _getVersion);
+
+ static bool _isSafari(NavigatorProvider navigator) {
+ return navigator.vendor.contains('Apple');
+ }
+
+ static Version _getVersion(NavigatorProvider navigator) {
+ Match match = new RegExp(r'Version/(\d+)\.(\d+)\.(\d+)')
+ .firstMatch(navigator.appVersion);
+ var major = int.parse(match.group(1));
+ var minor = int.parse(match.group(2));
+ var patch = int.parse(match.group(3));
+ return new Version(major, minor, patch);
+ }
+}
+
+class _InternetExplorer extends Browser {
+ _InternetExplorer()
+ : super('Internet Explorer', _isInternetExplorer, _getVersion);
+
+ static bool _isInternetExplorer(NavigatorProvider navigator) {
+ return navigator.appName.contains('Microsoft') ||
+ navigator.appVersion.contains('Trident') ||
+ navigator.appVersion.contains('Edge');
+ }
+
+ static Version _getVersion(NavigatorProvider navigator) {
+ Match match =
+ new RegExp(r'MSIE (\d+)\.(\d+);').firstMatch(navigator.appVersion);
+ if (match != null) {
+ var major = int.parse(match.group(1));
+ var minor = int.parse(match.group(2));
+ return new Version(major, minor, 0);
+ }
+
+ match = new RegExp(r'rv[: ](\d+)\.(\d+)').firstMatch(navigator.appVersion);
+ if (match != null) {
+ var major = int.parse(match.group(1));
+ var minor = int.parse(match.group(2));
+ return new Version(major, minor, 0);
+ }
+
+ match = new RegExp(r'Edge/(\d+)\.(\d+)$').firstMatch(navigator.appVersion);
+ if (match != null) {
+ var major = int.parse(match.group(1));
+ var minor = int.parse(match.group(2));
+ return new Version(major, minor, 0);
+ }
+
+ return new Version(0, 0, 0);
+ }
+}
diff --git a/lib/src/navigator.dart b/lib/src/navigator.dart
new file mode 100644
index 0000000..efd192d
--- /dev/null
+++ b/lib/src/navigator.dart
@@ -0,0 +1,17 @@
+library platform_detect.navigator;
+
+/// Abstraction over window.navigator so we can run tests in the VM
+abstract class NavigatorProvider {
+ String get vendor;
+ String get appVersion;
+ String get appName;
+ String get userAgent;
+}
+
+/// Simple implementation that enables ease of unit testing
+class TestNavigator implements NavigatorProvider {
+ String vendor = '';
+ String appVersion = '';
+ String appName = '';
+ String userAgent = '';
+}
diff --git a/lib/src/operating_system.dart b/lib/src/operating_system.dart
new file mode 100644
index 0000000..ba14f04
--- /dev/null
+++ b/lib/src/operating_system.dart
@@ -0,0 +1,49 @@
+library platform_detect.operating_system;
+
+import 'package:platform_detect/src/navigator.dart';
+
+/// Matches an operating system name with how it is represented in window.navigator
+class OperatingSystem {
+ static NavigatorProvider navigator;
+
+ static OperatingSystem getCurrentOperatingSystem() {
+ return _knownSystems.firstWhere(
+ (system) => system._matchesNavigator(navigator),
+ orElse: () => UnknownOS);
+ }
+
+ static OperatingSystem UnknownOS = new OperatingSystem('Unknown', null);
+
+ final String name;
+ final Function _matchesNavigator;
+
+ OperatingSystem(this.name, bool matchesNavigator(NavigatorProvider navigator))
+ : this._matchesNavigator = matchesNavigator;
+
+ static List _knownSystems = [_mac, _windows, _unix, _linux];
+
+ get isMac => this == _mac;
+ get isWindows => this == _windows;
+ get isUnix => this == _unix;
+ get isLinux => this == _linux;
+
+ static OperatingSystem _mac =
+ new OperatingSystem('Mac', (NavigatorProvider navigator) {
+ return navigator.appVersion.contains('Mac');
+ });
+
+ static OperatingSystem _windows =
+ new OperatingSystem('Windows', (NavigatorProvider navigator) {
+ return navigator.appVersion.contains('Win');
+ });
+
+ static OperatingSystem _unix =
+ new OperatingSystem('Unix', (NavigatorProvider navigator) {
+ return navigator.appVersion.contains('X11');
+ });
+
+ static OperatingSystem _linux =
+ new OperatingSystem('Linux', (NavigatorProvider navigator) {
+ return navigator.appVersion.contains('Linux');
+ });
+}
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..6c1cbbf
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,21 @@
+name: platform_detect
+description: A lightweight library for detecting the running browser and OS
+version: 0.0.1
+author: Workiva Client Platform Team
+homepage: https://github.com/Workiva/platform_detect
+documentation: https://docs.workiva.org/platform_detect/latest/
+publish_to: https://pub.workiva.org
+
+environment:
+ sdk: ">=1.12.1 <2.0.0"
+
+dependencies:
+ pub_semver: ^1.0.0
+
+dev_dependencies:
+ dartdoc: ^0.9.7
+ dart_style: 0.2.10
+ coverage: ^0.7.9
+ browser: any
+ dart_dev: ^1.4.2
+ test: ^0.12.0
diff --git a/smithy.yml b/smithy.yml
new file mode 100644
index 0000000..4e2d873
--- /dev/null
+++ b/smithy.yml
@@ -0,0 +1,24 @@
+project: dart
+language: dart
+
+# dart 1.19.1, built from https://github.com/Workiva/smithy-runner-dart/tree/0.0.4
+runner_image: drydock-prod.workiva.org/workiva/smithy-runner-dart:74173
+
+script:
+ - pub get
+ - pub run dart_dev format --check
+ - pub run dart_dev analyze
+ - pub run dart_dev test
+
+after_script:
+ - pub run dart_dev docs --no-open
+ - cd doc/api && tar -zcvf api.tar.gz * && cd ../..
+ - tar czvf platform_detect.pub.tgz LICENSE README.md pubspec.yaml lib/
+
+artifacts:
+ build:
+ - ./pubspec.lock
+ documentation:
+ - ./doc/api/api.tar.gz
+ pub:
+ - ./platform_detect.pub.tgz
\ No newline at end of file
diff --git a/test/browser_test.dart b/test/browser_test.dart
new file mode 100644
index 0000000..bd1df2f
--- /dev/null
+++ b/test/browser_test.dart
@@ -0,0 +1,100 @@
+@TestOn('vm')
+import 'package:platform_detect/src/browser.dart';
+import 'package:platform_detect/src/navigator.dart';
+import 'package:pub_semver/pub_semver.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('browser detects', () {
+ tearDown(() {
+ Browser.navigator = null;
+ });
+
+ test('Unknown Browser', () {
+ Browser.navigator = new TestNavigator();
+ var browser = Browser.getCurrentBrowser();
+ expect(browser.name, Browser.UnknownBrowser.name);
+ expect(browser.version, Browser.UnknownBrowser.version);
+ expect(browser.isChrome, false);
+ expect(browser.isFirefox, false);
+ expect(browser.isSafari, false);
+ expect(browser.isInternetExplorer, false);
+ });
+
+ test('Fake Browser', () {
+ Browser browser = new Browser(
+ 'Fake', (_) => true, (_) => new Version(1, 1, 0));
+ expect(browser.name, 'Fake');
+ expect(browser.version, new Version(1, 1, 0));
+ expect(browser.isChrome, false);
+ expect(browser.isFirefox, false);
+ expect(browser.isSafari, false);
+ expect(browser.isInternetExplorer, false);
+ });
+
+ test('Chrome', () {
+ var navigator = new TestNavigator()
+ ..userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
+ ..appVersion = '5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36'
+ ..vendor = 'Google Inc.';
+ Browser.navigator = navigator;
+ Browser browser = Browser.getCurrentBrowser();
+
+ expect(browser.name, 'Chrome');
+ expect(browser.isChrome, true);
+ expect(browser.isFirefox, false);
+ expect(browser.isSafari, false);
+ expect(browser.isInternetExplorer, false);
+ expect(browser.version, new Version(53, 0, 2785, build: '143'));
+ });
+
+ test('Internet Explorer', () {
+ var navigator = new TestNavigator()
+ ..userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko'
+ ..appVersion = '5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; rv:11.0) like Gecko'
+ ..appName = 'Netscape';
+ Browser.navigator = navigator;
+ Browser browser = Browser.getCurrentBrowser();
+
+ expect(browser.name, 'Internet Explorer');
+ expect(browser.isChrome, false);
+ expect(browser.isFirefox, false);
+ expect(browser.isSafari, false);
+ expect(browser.isInternetExplorer, true);
+ expect(browser.version, new Version(11, 0, 0));
+ });
+
+ test('Firefox', () {
+ var navigator = new TestNavigator()
+ ..userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0'
+ ..appVersion = '5.0 (Macintosh)'
+ ..appName = 'Netscape';
+ Browser.navigator = navigator;
+ Browser browser = Browser.getCurrentBrowser();
+
+ expect(browser.name, 'Firefox');
+ expect(browser.isChrome, false);
+ expect(browser.isFirefox, true);
+ expect(browser.isSafari, false);
+ expect(browser.isInternetExplorer, false);
+ expect(browser.version, new Version(48, 0, 0));
+ });
+
+ test('Safari', () {
+ var navigator = new TestNavigator()
+ ..vendor = 'Apple Computer, Inc.'
+ ..userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.8 (KHTML, like Gecko) Version/9.1.3 Safari/601.7.8'
+ ..appVersion = '5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.8 (KHTML, like Gecko) Version/9.1.3 Safari/601.7.8'
+ ..appName = 'Netscape';
+ Browser.navigator = navigator;
+ Browser browser = Browser.getCurrentBrowser();
+
+ expect(browser.name, 'Safari');
+ expect(browser.isChrome, false);
+ expect(browser.isFirefox, false);
+ expect(browser.isSafari, true);
+ expect(browser.isInternetExplorer, false);
+ expect(browser.version, new Version(9, 1, 3));
+ });
+ });
+}
diff --git a/test/operating_system_test.dart b/test/operating_system_test.dart
new file mode 100644
index 0000000..4e8e8a8
--- /dev/null
+++ b/test/operating_system_test.dart
@@ -0,0 +1,62 @@
+@TestOn('vm')
+import 'package:platform_detect/src/operating_system.dart';
+import 'package:platform_detect/src/navigator.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('operating system detects', () {
+ tearDown(() {
+ OperatingSystem.navigator = null;
+ });
+
+ test('Unknown Operating System', () {
+ OperatingSystem.navigator = new TestNavigator();
+ var os = OperatingSystem.getCurrentOperatingSystem();
+ expect(os.name, OperatingSystem.UnknownOS.name);
+ expect(os.isMac, false);
+ expect(os.isWindows, false);
+ expect(os.isUnix, false);
+ expect(os.isLinux, false);
+ });
+
+ test('Windows Operating System', () {
+ OperatingSystem.navigator = new TestNavigator()..appVersion = 'Windows';
+ var os = OperatingSystem.getCurrentOperatingSystem();
+ expect(os.name, 'Windows');
+ expect(os.isMac, false);
+ expect(os.isWindows, true);
+ expect(os.isUnix, false);
+ expect(os.isLinux, false);
+ });
+
+ test('Mac Operating System', () {
+ OperatingSystem.navigator = new TestNavigator()..appVersion = 'Macintosh';
+ var os = OperatingSystem.getCurrentOperatingSystem();
+ expect(os.name, 'Mac');
+ expect(os.isMac, true);
+ expect(os.isWindows, false);
+ expect(os.isUnix, false);
+ expect(os.isLinux, false);
+ });
+
+ test('Unix Operating System', () {
+ OperatingSystem.navigator = new TestNavigator()..appVersion = 'X11';
+ var os = OperatingSystem.getCurrentOperatingSystem();
+ expect(os.name, 'Unix');
+ expect(os.isMac, false);
+ expect(os.isWindows, false);
+ expect(os.isUnix, true);
+ expect(os.isLinux, false);
+ });
+
+ test('Linux Operating System', () {
+ OperatingSystem.navigator = new TestNavigator()..appVersion = 'Linux';
+ var os = OperatingSystem.getCurrentOperatingSystem();
+ expect(os.name, 'Linux');
+ expect(os.isMac, false);
+ expect(os.isWindows, false);
+ expect(os.isUnix, false);
+ expect(os.isLinux, true);
+ });
+ });
+}