New Upstream Release - ruby-user-agent-parser

Ready changes

Summary

Merged new upstream version: 2.15.0 (was: 2.13.0).

Resulting package

Built on 2023-05-25T05:35 (took 6m55s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases ruby-user-agent-parser

Lintian Result

Diff

diff --git a/.coditsu/ci.yml b/.coditsu/ci.yml
new file mode 100644
index 0000000..2d13e27
--- /dev/null
+++ b/.coditsu/ci.yml
@@ -0,0 +1,3 @@
+repository_id: '19e90f01-0da7-45f2-b0a7-afc6ad40e30e'
+api_key: <%= ENV['CODITSU_API_KEY'] %>
+api_secret: <%= ENV['CODITSU_API_SECRET'] %>
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..7546c46
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,59 @@
+name: ci
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+    branches: [main]
+
+jobs:
+  specs:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        ruby:
+          - '3.2'
+          - '3.1'
+          - '3.0'
+          - 'jruby-9.3.8.0'
+        include:
+          - ruby: '3.2'
+            coverage: '1'
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          submodules: true
+      - name: Setup Ruby
+        uses: ruby/setup-ruby@v1
+        with:
+          ruby-version: ${{ matrix.ruby }}
+      - name: Update rubygems
+        run: |
+          gem update --system
+      - uses: actions/cache@v3
+        with:
+          path: vendor/bundle
+          key: bundle-use-ruby-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
+          restore-keys: |
+            bundle-use-ruby-${{ matrix.ruby }}-
+      - name: Bundle install
+        run: |
+          bundle config path vendor/bundle
+          bundle install --jobs 4 --retry 3
+      - name: Run specs
+        env:
+          SIMPLECOV: ${{ matrix.coverage }}
+        run: |
+          bundle exec rake test
+
+  # coditsu:
+  #    runs-on: ubuntu-latest
+  #    strategy:
+  #      fail-fast: false
+  #    steps:
+  #      - uses: actions/checkout@v2
+  #        with:
+  #          fetch-depth: 0
+  #      - name: Run Coditsu
+  #        run: \curl -sSL https://api.coditsu.io/run/ci | bash
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..92b9b7d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.bundle
+coverage
+vendor/bundle
+.coditsu/local.yml
+pkg
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..399d8f3
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/uap-core"]
+	path = vendor/uap-core
+	url = https://github.com/ua-parser/uap-core.git
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 0000000..be94e6f
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+3.2.2
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..f8b176d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,35 @@
+# master
+
+# 2.15.0 (2023-04-12)
+ * Expose `parse_os`, `parse_device` and `parse_ua` methods on `Parser`
+
+# 2.14.0 (2023-01-31)
+  * Sync with https://github.com/ua-parser/uap-core/commit/1ef0926f2b489cc929589c00f8b8a3efce25acc3
+
+# 2.13.0 (2022-10-21)
+  * Support loading multiple database files (via #70) (@misdoro)
+    * Support `patterns_path` argument but deprecate `pattern_path` attribute accessor
+      in `UserAgentParser::Parser`
+    * Add new `patterns_paths` array argument `UserAgentParser::Parser` to enable loading
+      multiple patterns files
+
+# 2.12.0 (2022-10-20)
+
+  * sync with https://github.com/ua-parser/uap-core/commit/dc85ab2628798538a2874dea4a9563f40a31f55a
+  * Memory optimization (via #104) (@casperisfine)
+
+# 2.11.0 (2022-04-18)
+  * Make user agent versions comparable (via #68) (@misdoro)
+
+# 2.10.0 (2022-04-18)
+  * sync with uap-core 09e9ccc
+
+# 2.9.0 (2022-01-27)
+  * sync with uap-core 0.15.0
+
+# 2.8.0 (2021-11-02)
+  * sync with uap-core 0.14.0
+  * drop support for ruby 2.4
+
+# 2.7.0 (2020-05-25)
+  * sync with uap-core 0.10.0
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..1b5a968
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org/'
+
+gemspec
+
+group :development, :test do
+  gem 'coveralls_reborn'
+  gem 'minitest'
+  gem 'rake'
+end
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..dbc1b2c
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,41 @@
+PATH
+  remote: .
+  specs:
+    user_agent_parser (2.15.0)
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    coveralls_reborn (0.27.0)
+      simplecov (~> 0.22.0)
+      term-ansicolor (~> 1.7)
+      thor (~> 1.2)
+      tins (~> 1.32)
+    docile (1.4.0)
+    minitest (5.18.0)
+    rake (13.0.6)
+    simplecov (0.22.0)
+      docile (~> 1.1)
+      simplecov-html (~> 0.11)
+      simplecov_json_formatter (~> 0.1)
+    simplecov-html (0.12.3)
+    simplecov_json_formatter (0.1.4)
+    sync (0.5.0)
+    term-ansicolor (1.7.1)
+      tins (~> 1.0)
+    thor (1.2.1)
+    tins (1.32.1)
+      sync
+
+PLATFORMS
+  java
+  ruby
+
+DEPENDENCIES
+  coveralls_reborn
+  minitest
+  rake
+  user_agent_parser!
+
+BUNDLED WITH
+   2.4.10
diff --git a/MIT-LICENSE b/MIT-LICENSE
index 313f6b5..72b5fba 100644
--- a/MIT-LICENSE
+++ b/MIT-LICENSE
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..16b43f5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,146 @@
+# UserAgentParser [![Build Status](https://github.com/ua-parser/uap-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/ua-parser/uap-ruby/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/ua-parser/uap-ruby/badge.svg)](https://coveralls.io/github/ua-parser/uap-ruby)
+
+UserAgentParser is a simple, comprehensive Ruby gem for parsing user agent strings. It uses [BrowserScope](http://www.browserscope.org/)'s [parsing patterns](https://github.com/ua-parser/uap-core).
+
+## Supported Rubies
+
+* Ruby 3.2
+* Ruby 3.1
+* Ruby 3.0
+* JRuby
+
+## Installation
+
+```bash
+$ gem install user_agent_parser
+```
+
+## Example usage
+
+```ruby
+require 'user_agent_parser'
+=> true
+user_agent = UserAgentParser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
+=> #<UserAgentParser::UserAgent IE 9.0 (Windows Vista)>
+user_agent.to_s
+=> "IE 9.0"
+user_agent.family
+=> "IE"
+user_agent.version.to_s
+=> "9.0"
+user_agent.version.major
+=> "9"
+user_agent.version.minor
+=> "0"
+user_agent.family == "IE" && user_agent.version >= "9"
+=> true
+operating_system = user_agent.os
+=> #<UserAgentParser::OperatingSystem Windows Vista>
+operating_system.to_s
+=> "Windows Vista"
+
+# Device information can also be determined from some devices
+user_agent = UserAgentParser.parse "Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G930T Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.0 Chrome/51.0.2704.106 Mobile Safari/537.36"
+=> #<UserAgentParser::UserAgent Samsung Internet 5.0 (Android 7.0) (Samsung SM-G930T)>
+user_agent.device.family
+=> "Samsung SM-G930T"
+user_agent.device.brand
+=> "Samsung"
+user_agent.device.model
+=> "SM-G930T"
+
+user_agent = UserAgentParser.parse "Mozilla/5.0 (iPad; CPU OS 10_2_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) GSA/23.1.148956103 Mobile/14D27 Safari/600.1.4"
+=> #<UserAgentParser::UserAgent Mobile Safari 10.2.1 (iOS 10.2.1) (iPad)>
+irb(main):026:0> user_agent.device.family
+=> "iPad"
+irb(main):027:0> user_agent.device.brand
+=> "Apple"
+irb(main):028:0> user_agent.device.model
+=> "iPad"
+
+
+# The parser database will be loaded and parsed on every call to
+# UserAgentParser.parse. To avoid this, instantiate your own Parser instance.
+parser = UserAgentParser::Parser.new
+parser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
+=> #<UserAgentParser::UserAgent IE 9.0 (Windows Vista)>
+parser.parse 'Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.5.24 Version/10.53'
+=> #<UserAgentParser::UserAgent Opera 10.53 (Windows XP)>
+```
+
+In a larger application, you could store a parser in a global to avoid repeat pattern loading:
+
+```ruby
+module MyApplication
+
+  # Instantiate the parser on load as it's quite expensive
+  USER_AGENT_PARSER = UserAgentParser::Parser.new
+
+  def self.user_agent_parser
+    USER_AGENT_PARSER
+  end
+
+end
+```
+
+## The pattern database
+
+The [ua-parser database](https://github.com/ua-parser/uap-core/blob/master/regexes.yaml) is included via a [git submodule](http://help.github.com/submodules/). To update the database the submodule needs to be updated and the gem re-released (pull requests for this are very welcome!).
+
+You can also specify the path to your own, updated and/or customised `regexes.yaml` file as a second argument to `UserAgentParser.parse`:
+
+```ruby
+UserAgentParser.parse(ua_string, patterns_path: '/some/path/to/regexes.yaml')
+```
+
+or when instantiating a `UserAgentParser::Parser`:
+
+```ruby
+UserAgentParser::Parser.new(patterns_path: '/some/path/to/regexes.yaml').parse(ua_string)
+```
+
+Extending the standard database is possible by providing multiple files in `patterns_paths` (plural) array argument:
+```ruby
+UserAgentParser::Parser.new(patterns_paths: [UserAgentParser::DefaultPatternsPath, '/some/path/to/regexes.yaml'])
+```
+
+## Command line tool
+
+The gem incldes a `user_agent_parser` bin command which will read from
+standard input, parse each line and print the result, for example:
+
+```bash
+$ cat > SOME-FILE-WITH-USER-AGENTS.txt
+USER_AGENT_1
+USER_AGENT_2
+...
+$ cat SOME-FILE-WITH-USER-AGENTS.txt | user_agent_parser --format '%f %M' | distribution
+```
+
+See `user_agent_parser -h` for more information.
+
+## Contributing
+
+1. Fork
+2. Hack
+3. `rake test`
+4. Send a pull request
+
+All accepted pull requests will earn you commit and release rights.
+
+## Releasing a new version
+
+1. Update the version in `user_agent_parser.gemspec`
+2. `git commit user_agent_parser.gemspec` with the following message format:
+
+        Version x.x.x
+
+        Changelog:
+        * Some new feature
+        * Some new bug fix
+3. `rake release`
+4. Create a [new Github release](https://github.com/ua-parser/uap-ruby/releases/new)
+
+## License
+
+MIT
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..4fec062
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'rake/testtask'
+require 'bundler'
+
+task default: :test
+
+desc 'Run tests'
+Rake::TestTask.new do |t|
+  t.warning = true
+  t.verbose = true
+  t.pattern = 'spec/*_spec.rb'
+end
+
+Bundler::GemHelper.install_tasks
+
+# Does not actually get all families, as some are only listed in the regexes,
+# but gives you a pretty good idea of what will be returned.
+desc 'Lists all unique family names for browsers and operating systems.'
+task :families do
+  require 'pathname'
+  require 'pp'
+
+  root = Pathname(__FILE__).dirname
+  path = root.join('vendor', 'uap-core')
+
+  browser_families = paths_to_families(
+    [
+      # path.join('tests', 'test_ua.yaml'),
+      path.join('test_resources', 'firefox_user_agent_strings.yaml'),
+      path.join('test_resources', 'pgts_browser_list.yaml')
+    ]
+  )
+
+  os_families = paths_to_families(
+    [
+      # path.join('tests', 'test_os.yaml'),
+      path.join('test_resources', 'additional_os_tests.yaml')
+    ]
+  )
+
+  device_families = paths_to_families(
+    [
+      # path.join('tests', 'test_device.yaml'),
+    ]
+  )
+
+  puts "\n\nBrowser Families"
+  puts browser_families.inspect
+
+  puts "\n\nOS Families"
+  puts os_families.inspect
+
+  puts "\n\nDevice Families"
+  puts device_families.inspect
+
+  puts "\n\n"
+  puts "Browser Family Count: #{browser_families.size}"
+  puts "OS Family Count: #{os_families.size}"
+  puts "Device Family Count: #{device_families.size}"
+end
+
+def paths_to_families(paths)
+  require 'yaml'
+
+  families = []
+
+  paths.each do |path|
+    data = YAML.load_file(path)
+    test_cases = data.fetch('test_cases')
+    families.concat test_cases.map { |row| row['family'] }
+  end
+
+  families.compact.uniq.sort
+end
diff --git a/debian/changelog b/debian/changelog
index 976aabd..539e429 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,12 +1,14 @@
-ruby-user-agent-parser (2.5.1-3) UNRELEASED; urgency=low
+ruby-user-agent-parser (2.15.0-1) UNRELEASED; urgency=low
 
   * Set field Upstream-Contact in debian/copyright.
   * Remove obsolete field Contact from debian/upstream/metadata (already present
     in machine-readable debian/copyright).
   * Bump debhelper from old 12 to 13.
   * Update standards version to 4.5.1, no changes needed.
+  * New upstream release.
+  * New upstream release.
 
- -- Debian Janitor <janitor@jelmer.uk>  Sat, 25 Jul 2020 15:26:29 -0000
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 25 May 2023 05:28:54 -0000
 
 ruby-user-agent-parser (2.5.1-2) unstable; urgency=medium
 
diff --git a/lib/user_agent_parser.rb b/lib/user_agent_parser.rb
index 5fb2fda..d0ec6aa 100644
--- a/lib/user_agent_parser.rb
+++ b/lib/user_agent_parser.rb
@@ -10,7 +10,7 @@ module UserAgentParser
   DefaultPatternsPath = File.join(File.dirname(__FILE__), '../vendor/uap-core/regexes.yaml')
 
   # Parse the given +user_agent_string+, returning a +UserAgent+
-  def self.parse(user_agent_string, options = {})
-    Parser.new(options).parse(user_agent_string)
+  def self.parse(user_agent_string, **args)
+    Parser.new(**args).parse(user_agent_string)
   end
 end
diff --git a/lib/user_agent_parser/device.rb b/lib/user_agent_parser/device.rb
index 0c5d0ec..0ff4f04 100644
--- a/lib/user_agent_parser/device.rb
+++ b/lib/user_agent_parser/device.rb
@@ -2,12 +2,14 @@
 
 module UserAgentParser
   class Device
+    DEFAULT_FAMILY = 'Other'
+
     attr_reader :family, :model, :brand
 
     alias name family
 
     def initialize(family = nil, model = nil, brand = nil)
-      @family = family || 'Other'
+      @family = family || DEFAULT_FAMILY
       @model = model || @family
       @brand = brand
     end
diff --git a/lib/user_agent_parser/operating_system.rb b/lib/user_agent_parser/operating_system.rb
index 219e478..c2f4220 100644
--- a/lib/user_agent_parser/operating_system.rb
+++ b/lib/user_agent_parser/operating_system.rb
@@ -2,11 +2,13 @@
 
 module UserAgentParser
   class OperatingSystem
+    DEFAULT_FAMILY = 'Other'
+
     attr_reader :family, :version
 
     alias name family
 
-    def initialize(family = 'Other', version = nil)
+    def initialize(family = DEFAULT_FAMILY, version = nil)
       @family = family
       @version = version
     end
diff --git a/lib/user_agent_parser/parser.rb b/lib/user_agent_parser/parser.rb
index 5b3dee8..18f5f1e 100644
--- a/lib/user_agent_parser/parser.rb
+++ b/lib/user_agent_parser/parser.rb
@@ -4,6 +4,8 @@ require 'yaml'
 
 module UserAgentParser
   class Parser
+    extend Gem::Deprecate
+
     FAMILY_REPLACEMENT_KEYS = %w[
       family_replacement
       v1_replacement
@@ -22,11 +24,13 @@ module UserAgentParser
 
     private_constant :FAMILY_REPLACEMENT_KEYS, :OS_REPLACEMENT_KEYS
 
-    attr_reader :patterns_path
+    attr_reader :patterns_paths
+
+    def initialize(patterns_path: nil, patterns_paths: [])
+      @patterns_paths = [patterns_path, *patterns_paths].compact
+      @patterns_paths = [UserAgentParser::DefaultPatternsPath] if @patterns_paths.empty?
 
-    def initialize(options = {})
-      @patterns_path = options[:patterns_path] || UserAgentParser::DefaultPatternsPath
-      @ua_patterns, @os_patterns, @device_patterns = load_patterns(patterns_path)
+      @ua_patterns, @os_patterns, @device_patterns = load_patterns(@patterns_paths)
     end
 
     def parse(user_agent)
@@ -35,19 +39,24 @@ module UserAgentParser
       parse_ua(user_agent, os, device)
     end
 
-    private
-
-    def load_patterns(path)
-      yml = YAML.load_file(path)
+    def parse_os(user_agent)
+      pattern, match = first_pattern_match(@os_patterns, user_agent)
 
-      # Parse all the regexs
-      yml.each_pair do |_type, patterns|
-        patterns.each do |pattern|
-          pattern[:regex] = Regexp.new(pattern['regex'], pattern['regex_flag'] == 'i')
-        end
+      if match
+        os_from_pattern_match(pattern, match)
+      else
+        OperatingSystem.new
       end
+    end
+
+    def parse_device(user_agent)
+      pattern, match = first_pattern_match(@device_patterns, user_agent)
 
-      [yml['user_agent_parsers'], yml['os_parsers'], yml['device_parsers']]
+      if match
+        device_from_pattern_match(pattern, match)
+      else
+        Device.new
+      end
     end
 
     def parse_ua(user_agent, os = nil, device = nil)
@@ -60,44 +69,48 @@ module UserAgentParser
       end
     end
 
-    def parse_os(user_agent)
-      pattern, match = first_pattern_match(@os_patterns, user_agent)
+    def patterns_path
+      patterns_paths.first
+    end
+    deprecate :patterns_path, :patterns_paths, 2022, 12
 
-      if match
-        os_from_pattern_match(pattern, match)
-      else
-        OperatingSystem.new
+    private
+
+    def load_patterns(patterns_paths)
+      patterns_paths.each_with_object([[], [], []]) do |path, patterns|
+        ua_patterns, os_patterns, device_patterns = load_patterns_file(path)
+        patterns[0] += ua_patterns
+        patterns[1] += os_patterns
+        patterns[2] += device_patterns
       end
     end
 
-    def parse_device(user_agent)
-      pattern, match = first_pattern_match(@device_patterns, user_agent)
-
-      if match
-        device_from_pattern_match(pattern, match)
-      else
-        Device.new
+    def load_patterns_file(path)
+      yml = begin
+        YAML.load_file(path, freeze: true)
+      rescue ArgumentError
+        YAML.load_file(path)
       end
+      [
+        parse_pattern(yml['user_agent_parsers']),
+        parse_pattern(yml['os_parsers']),
+        parse_pattern(yml['device_parsers']),
+      ]
     end
 
-    if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
-      def first_pattern_match(patterns, value)
-        patterns.each do |pattern|
-          if pattern[:regex].match?(value)
-            return [pattern, pattern[:regex].match(value)]
-          end
-        end
-        nil
+    def parse_pattern(patterns)
+      patterns.map do |pattern|
+        pattern = pattern.dup
+        pattern[:regex] = Regexp.new(pattern.delete('regex'), pattern.delete('regex_flag') == 'i')
+        pattern
       end
-    else
-      def first_pattern_match(patterns, value)
-        patterns.each do |pattern|
-          if (match = pattern[:regex].match(value))
-            return [pattern, match]
-          end
-        end
-        nil
+    end
+
+    def first_pattern_match(patterns, value)
+      patterns.each do |pattern|
+        return [pattern, pattern[:regex].match(value)] if pattern[:regex].match?(value)
       end
+      nil
     end
 
     def user_agent_from_pattern_match(pattern, match, os = nil, device = nil)
diff --git a/lib/user_agent_parser/user_agent.rb b/lib/user_agent_parser/user_agent.rb
index ff9df9a..60421ce 100644
--- a/lib/user_agent_parser/user_agent.rb
+++ b/lib/user_agent_parser/user_agent.rb
@@ -2,12 +2,14 @@
 
 module UserAgentParser
   class UserAgent
+    DEFAULT_FAMILY = 'Other'
+
     attr_reader :family, :version, :os, :device
 
     alias name family
 
     def initialize(family = nil, version = nil, os = nil, device = nil)
-      @family = family || 'Other'
+      @family = family || DEFAULT_FAMILY
       @version = version
       @os = os
       @device = device
diff --git a/lib/user_agent_parser/version.rb b/lib/user_agent_parser/version.rb
index 3b513b1..abb9299 100644
--- a/lib/user_agent_parser/version.rb
+++ b/lib/user_agent_parser/version.rb
@@ -1,10 +1,14 @@
 # frozen_string_literal: true
 
+require 'rubygems/version'
+
 module UserAgentParser
   class Version
+    include Comparable
+
     # Private: Regex used to split string version string into major, minor,
     # patch, and patch_minor.
-    SEGMENTS_REGEX = /\d+\-\d+|\d+[a-zA-Z]+$|\d+|[A-Za-z][0-9A-Za-z-]*$/
+    SEGMENTS_REGEX = /\d+\-\d+|\d+[a-zA-Z]+$|\d+|[A-Za-z][0-9A-Za-z-]*$/.freeze
 
     attr_reader :version
     alias to_s version
@@ -45,9 +49,10 @@ module UserAgentParser
         version == other.version
     end
 
-    alias == eql?
+    def <=>(other)
+      Gem::Version.new(version).<=>(Gem::Version.new(other.to_s))
+    end
 
-    # Private
     def segments
       @segments ||= version.scan(SEGMENTS_REGEX)
     end
diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb
new file mode 100644
index 0000000..31a72f2
--- /dev/null
+++ b/spec/cli_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+require 'user_agent_parser/cli'
+
+describe UserAgentParser::Cli do
+  let(:cli) { UserAgentParser::Cli.new(user_agent, options) }
+  let(:options) { {} }
+  let(:parser) { UserAgentParser::Parser.new }
+  let(:user_agent) do
+    parser.parse('Mozilla/5.0 (iPad; CPU OS 6_0_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A523 Safari/8536.25')
+  end
+
+  it 'prints family and version when no options' do
+    _(cli.run!).must_equal('Mobile Safari 6.0')
+  end
+
+  describe 'invalid version' do
+    let(:user_agent) do
+      parser.parse('Mozilla/5.0 (iPad; CPU OS like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/XYZ Mobile/10A523 Safari/8536.25')
+    end
+
+    describe '--version' do
+      let(:options) { { version: true } }
+
+      it 'returns nil' do
+        _(cli.run!).must_be_nil
+      end
+    end
+
+    describe '--major' do
+      let(:options) { { major: true } }
+
+      it 'returns nil' do
+        _(cli.run!).must_be_nil
+      end
+    end
+
+    describe '--minor' do
+      let(:options) { { minor: true } }
+
+      it 'returns nil' do
+        _(cli.run!).must_be_nil
+      end
+    end
+
+    describe '--format' do
+      let(:options) { { format: '%n|%f|%v|%M|%m|%o' } }
+
+      it 'returns string without versions' do
+        _(cli.run!).must_equal('Mobile Safari|Mobile Safari||||iOS')
+      end
+    end
+  end
+
+  describe '--name' do
+    let(:options) { { name: true } }
+
+    it 'returns name only' do
+      _(cli.run!).must_equal('Mobile Safari')
+    end
+  end
+
+  describe '--family' do
+    let(:options) { { family: true } }
+
+    it 'returns family only' do
+      _(cli.run!).must_equal('Mobile Safari')
+    end
+  end
+
+  describe '--version' do
+    let(:options) { { version: true } }
+
+    it 'returns version only' do
+      _(cli.run!).must_equal('6.0')
+    end
+  end
+
+  describe '--major' do
+    let(:options) { { major: true } }
+
+    it 'returns major version only' do
+      _(cli.run!).must_equal('6')
+    end
+  end
+
+  describe '--minor' do
+    let(:options) { { minor: true } }
+
+    it 'returns minor version only' do
+      _(cli.run!).must_equal('0')
+    end
+  end
+
+  describe '--os' do
+    let(:options) { { os: true } }
+
+    it 'returns operating system only' do
+      _(cli.run!).must_equal('iOS 6.0.1')
+    end
+  end
+
+  describe '--format' do
+    let(:options) { { format: '%n|%v|%M|%m|%o' } }
+
+    it 'return string with correct replacements' do
+      _(cli.run!).must_equal('Mobile Safari|6.0|6|0|iOS 6.0.1')
+    end
+  end
+end
diff --git a/spec/custom_regexes.yaml b/spec/custom_regexes.yaml
new file mode 100644
index 0000000..13266d2
--- /dev/null
+++ b/spec/custom_regexes.yaml
@@ -0,0 +1,17 @@
+user_agent_parsers:
+  - regex: 'Any.*'
+    family_replacement: 'Custom browser'
+    v1_replacement: '1'
+    v2_replacement: '2'
+    v3_replacement: '3'
+    v4_replacement: '4'
+
+os_parsers:
+  - regex: 'Any.*'
+    os_replacement: 'Custom OS'
+    os_v1_replacement: '1'
+    os_v2_replacement: '2'
+
+device_parsers:
+  - regex: 'Any.*'
+    device_replacement: 'Custom device'
diff --git a/spec/device_spec.rb b/spec/device_spec.rb
new file mode 100644
index 0000000..d339dd8
--- /dev/null
+++ b/spec/device_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+describe UserAgentParser::Device do
+  describe '#name' do
+    it 'returns family' do
+      os = UserAgentParser::Device.new('iPod')
+      _(os.name).must_equal os.family
+    end
+  end
+
+  describe '#to_s' do
+    it 'returns a string of just the family' do
+      os = UserAgentParser::Device.new('iPod')
+      _(os.to_s).must_equal 'iPod'
+    end
+  end
+
+  describe '#==' do
+    it 'returns true for same family' do
+      device1 = UserAgentParser::Device.new('iPod')
+      device2 = UserAgentParser::Device.new('iPod')
+      _(device1).must_equal device2
+    end
+
+    it 'returns false different family' do
+      device1 = UserAgentParser::Device.new('iPod')
+      device2 = UserAgentParser::Device.new('iPad')
+      _(device1).wont_equal device2
+    end
+  end
+
+  describe '#eql?' do
+    it 'returns true for same family' do
+      device1 = UserAgentParser::Device.new('iPod')
+      device2 = UserAgentParser::Device.new('iPod')
+      assert_equal true, device1.eql?(device2)
+    end
+
+    it 'returns false different family' do
+      device1 = UserAgentParser::Device.new('iPod')
+      device2 = UserAgentParser::Device.new('iPad')
+      assert_equal false, device1.eql?(device2)
+    end
+  end
+
+  describe '#inspect' do
+    it 'returns class family and instance to_s' do
+      device = UserAgentParser::Device.new('iPod')
+      _(device.inspect.to_s).must_equal '#<UserAgentParser::Device iPod>'
+    end
+  end
+end
diff --git a/spec/operating_system_spec.rb b/spec/operating_system_spec.rb
new file mode 100644
index 0000000..d2abb5e
--- /dev/null
+++ b/spec/operating_system_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+describe UserAgentParser::OperatingSystem do
+  describe '#name' do
+    it 'returns family' do
+      os = UserAgentParser::OperatingSystem.new('Windows')
+      _(os.name).must_equal os.family
+    end
+  end
+
+  describe '#to_s' do
+    it 'returns a string of just the family' do
+      os = UserAgentParser::OperatingSystem.new('Windows')
+      _(os.to_s).must_equal 'Windows'
+    end
+
+    it 'returns a string of family and version' do
+      version = UserAgentParser::Version.new('7')
+      os = UserAgentParser::OperatingSystem.new('Windows', version)
+      _(os.to_s).must_equal 'Windows 7'
+    end
+  end
+
+  describe '#==' do
+    it "returns true for same user agents across different O/S's" do
+      version = UserAgentParser::Version.new('7')
+      os1 = UserAgentParser::OperatingSystem.new('Windows', version)
+      os2 = UserAgentParser::OperatingSystem.new('Windows', version)
+      _(os1).must_equal os2
+    end
+
+    it 'returns false for same family, different versions' do
+      seven = UserAgentParser::Version.new('7')
+      eight = UserAgentParser::Version.new('8')
+      os1 = UserAgentParser::OperatingSystem.new('Windows', seven)
+      os2 = UserAgentParser::OperatingSystem.new('Windows', eight)
+      _(os1).wont_equal os2
+    end
+
+    it 'returns false for different family, same version' do
+      version = UserAgentParser::Version.new('7')
+      os1 = UserAgentParser::OperatingSystem.new('Windows', version)
+      os2 = UserAgentParser::OperatingSystem.new('Blah', version)
+      _(os1).wont_equal os2
+    end
+  end
+
+  describe '#eql?' do
+    it "returns true for same user agents across different O/S's" do
+      version = UserAgentParser::Version.new('7')
+      os1 = UserAgentParser::OperatingSystem.new('Windows', version)
+      os2 = UserAgentParser::OperatingSystem.new('Windows', version)
+      assert_equal true, os1.eql?(os2)
+    end
+
+    it 'returns false for same family, different versions' do
+      seven = UserAgentParser::Version.new('7')
+      eight = UserAgentParser::Version.new('8')
+      os1 = UserAgentParser::OperatingSystem.new('Windows', seven)
+      os2 = UserAgentParser::OperatingSystem.new('Windows', eight)
+      assert_equal false, os1.eql?(os2)
+    end
+
+    it 'returns false for different family, same version' do
+      version = UserAgentParser::Version.new('7')
+      os1 = UserAgentParser::OperatingSystem.new('Windows', version)
+      os2 = UserAgentParser::OperatingSystem.new('Blah', version)
+      assert_equal false, os1.eql?(os2)
+    end
+  end
+
+  describe '#inspect' do
+    it 'returns class family and instance to_s' do
+      version = UserAgentParser::Version.new('10.7.4')
+      os = UserAgentParser::OperatingSystem.new('OS X', version)
+      _(os.inspect.to_s).must_equal '#<UserAgentParser::OperatingSystem OS X 10.7.4>'
+    end
+  end
+end
diff --git a/spec/other_regexes.yaml b/spec/other_regexes.yaml
new file mode 100644
index 0000000..44e9138
--- /dev/null
+++ b/spec/other_regexes.yaml
@@ -0,0 +1,17 @@
+user_agent_parsers:
+  - regex: 'Other.*'
+    family_replacement: 'Other browser'
+    v1_replacement: '1'
+    v2_replacement: '2'
+    v3_replacement: '3'
+    v4_replacement: '4'
+
+os_parsers:
+  - regex: 'Other.*'
+    os_replacement: 'Other OS'
+    os_v1_replacement: '1'
+    os_v2_replacement: '2'
+
+device_parsers:
+  - regex: 'Other.*'
+    device_replacement: 'Other device'
diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb
new file mode 100644
index 0000000..e5edeb3
--- /dev/null
+++ b/spec/parser_spec.rb
@@ -0,0 +1,256 @@
+# frozen_string_literal: true
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+require 'yaml'
+
+describe UserAgentParser::Parser do
+  PARSER = UserAgentParser::Parser.new
+
+  # Some Ruby versions (JRuby) need sanitised test names, as some chars screw
+  # up the test method definitions
+  def self.test_case_to_test_name(test_case)
+    name = "#{test_case['user_agent_string']}_#{test_case['family']}"
+    name.gsub(/[^a-z0-9_.-]/i, '_').squeeze('_')
+  end
+
+  def self.file_to_test_cases(file)
+    file_to_yaml(file)['test_cases'].map do |test_case|
+      {
+        'user_agent_string' => test_case['user_agent_string'],
+        'family' => test_case['family'],
+        'major' => test_case['major'],
+        'minor' => test_case['minor'],
+        'patch' => test_case['patch'],
+        'patch_minor' => test_case['patch_minor'],
+        'brand' => test_case['brand'],
+        'model' => test_case['model']
+      }
+    end.reject do |test_case|
+      # We don't do the hacky javascript user agent overrides
+      test_case.key?('js_ua') ||
+        test_case['family'] == 'IE Platform Preview' ||
+        test_case['user_agent_string'].include?('chromeframe;')
+    end
+  end
+
+  def self.file_to_yaml(resource)
+    uap_path = File.expand_path('../../vendor/uap-core', __FILE__)
+    resource_path = File.join(uap_path, resource)
+    YAML.load_file(resource_path)
+  end
+
+  def self.user_agent_test_cases
+    file_to_test_cases('test_resources/firefox_user_agent_strings.yaml')
+    file_to_test_cases('tests/test_ua.yaml')
+  end
+
+  def self.operating_system_test_cases
+    file_to_test_cases('tests/test_os.yaml') +
+      file_to_test_cases('test_resources/additional_os_tests.yaml')
+  end
+
+  def self.device_test_cases
+    file_to_test_cases('tests/test_device.yaml')
+  end
+
+  def custom_patterns_path
+    File.join(File.dirname(__FILE__), 'custom_regexes.yaml')
+  end
+
+  def other_patterns_path
+    File.join(File.dirname(__FILE__), 'other_regexes.yaml')
+  end
+
+  describe '::parse' do
+    it 'parses a UA' do
+      ua = UserAgentParser.parse('Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3')
+      _(ua.family).must_equal('Safari')
+    end
+    it 'accepts a custom patterns path' do
+      ua = UserAgentParser.parse('Any user agent string', patterns_path: custom_patterns_path)
+      _(ua.family).must_equal('Custom browser')
+    end
+  end
+
+  describe '#initialize with a custom patterns path' do
+    it 'accepts a single patterns_path string' do
+      parser = UserAgentParser::Parser.new(patterns_path: custom_patterns_path)
+      ua = parser.parse('Any user agent string')
+
+      _(parser.patterns_paths).must_equal([custom_patterns_path])
+      _(parser.patterns_path).must_equal(custom_patterns_path)
+
+      _(ua.family).must_equal('Custom browser')
+      _(ua.version.major).must_equal('1')
+      _(ua.version.minor).must_equal('2')
+      _(ua.version.patch).must_equal('3')
+      _(ua.version.patch_minor).must_equal('4')
+
+      _(ua.os.family).must_equal('Custom OS')
+      _(ua.os.version.major).must_equal('1')
+      _(ua.os.version.minor).must_equal('2')
+
+      _(ua.device.family).must_equal('Custom device')
+    end
+
+    it 'accepts patterns_paths array' do
+      patterns_paths = [custom_patterns_path, other_patterns_path]
+      parser = UserAgentParser::Parser.new(patterns_paths: patterns_paths)
+
+      _(parser.patterns_paths).must_equal(patterns_paths)
+      _(parser.patterns_path).must_equal(custom_patterns_path)
+
+      ua = parser.parse('Any user agent string')
+      oua = parser.parse('Other user agent string')
+
+      _(ua.family).must_equal('Custom browser')
+      _(oua.family).must_equal('Other browser')
+    end
+  end
+
+  describe '#parse' do
+    user_agent_test_cases.each do |test_case|
+      it "parses UA for #{test_case_to_test_name(test_case)}" do
+        user_agent = PARSER.parse(test_case['user_agent_string'])
+
+        if test_case['family']
+          _(user_agent.family).must_equal_test_case_property(test_case, 'family')
+        end
+
+        if test_case['major']
+          _(user_agent.version.major).must_equal_test_case_property(test_case, 'major')
+        end
+
+        if test_case['minor']
+          _(user_agent.version.minor).must_equal_test_case_property(test_case, 'minor')
+        end
+
+        if test_case['patch']
+          _(user_agent.version.patch).must_equal_test_case_property(test_case, 'patch')
+        end
+      end
+    end
+
+    operating_system_test_cases.each do |test_case|
+      it "parses OS for #{test_case_to_test_name(test_case)}" do
+        user_agent = PARSER.parse(test_case['user_agent_string'])
+        operating_system = user_agent.os
+
+        if test_case['family']
+          _(operating_system.family).must_equal_test_case_property(test_case, 'family')
+        end
+
+        if test_case['major']
+          _(operating_system.version.major).must_equal_test_case_property(test_case, 'major')
+        end
+
+        if test_case['minor']
+          _(operating_system.version.minor).must_equal_test_case_property(test_case, 'minor')
+        end
+
+        if test_case['patch']
+          _(operating_system.version.patch).must_equal_test_case_property(test_case, 'patch')
+        end
+
+        if test_case['patch_minor']
+          _(operating_system.version.patch_minor).must_equal_test_case_property(test_case, 'patch_minor')
+        end
+      end
+    end
+
+    device_test_cases.each do |test_case|
+      it "parses device for #{test_case_to_test_name(test_case)}" do
+        user_agent = PARSER.parse(test_case['user_agent_string'])
+        device = user_agent.device
+
+        if test_case['family']
+          _(device.family).must_equal_test_case_property(test_case, 'family')
+        end
+
+        if test_case['model']
+          _(device.model).must_equal_test_case_property(test_case, 'model')
+        end
+
+        if test_case['brand']
+          _(device.brand).must_equal_test_case_property(test_case, 'brand')
+        end
+      end
+    end
+  end
+
+  describe '#parse_os' do
+    operating_system_test_cases.each do |test_case|
+      it "parses OS for #{test_case_to_test_name(test_case)}" do
+        operating_system = PARSER.parse_os(test_case['user_agent_string'])
+
+        if test_case['family']
+          _(operating_system.family).must_equal_test_case_property(test_case, 'family')
+        end
+
+        if test_case['major']
+          _(operating_system.version.major).must_equal_test_case_property(test_case, 'major')
+        end
+
+        if test_case['minor']
+          _(operating_system.version.minor).must_equal_test_case_property(test_case, 'minor')
+        end
+
+        if test_case['patch']
+          _(operating_system.version.patch).must_equal_test_case_property(test_case, 'patch')
+        end
+
+        if test_case['patch_minor']
+          _(operating_system.version.patch_minor).must_equal_test_case_property(test_case, 'patch_minor')
+        end
+      end
+    end
+  end
+
+  describe '#parse_device' do
+    device_test_cases.each do |test_case|
+      it "parses device for #{test_case_to_test_name(test_case)}" do
+        device = PARSER.parse_device(test_case['user_agent_string'])
+
+        if test_case['family']
+          _(device.family).must_equal_test_case_property(test_case, 'family')
+        end
+
+        if test_case['model']
+          _(device.model).must_equal_test_case_property(test_case, 'model')
+        end
+
+        if test_case['brand']
+          _(device.brand).must_equal_test_case_property(test_case, 'brand')
+        end
+      end
+    end
+  end
+
+  describe '#parse_ua' do
+    user_agent_test_cases.each do |test_case|
+      it "parses UA for #{test_case_to_test_name(test_case)}" do
+        user_agent = PARSER.parse_ua(test_case['user_agent_string'])
+
+        assert_nil user_agent.os
+        assert_nil user_agent.device
+
+        if test_case['family']
+          _(user_agent.family).must_equal_test_case_property(test_case, 'family')
+        end
+
+        if test_case['major']
+          _(user_agent.version.major).must_equal_test_case_property(test_case, 'major')
+        end
+
+        if test_case['minor']
+          _(user_agent.version.minor).must_equal_test_case_property(test_case, 'minor')
+        end
+
+        if test_case['patch']
+          _(user_agent.version.patch).must_equal_test_case_property(test_case, 'patch')
+        end
+      end
+    end
+  end
+
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..7aeb4e1
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+if ENV['SIMPLECOV'] == '1'
+  require 'coveralls'
+  require 'simplecov'
+
+  SimpleCov.formatter = Coveralls::SimpleCov::Formatter
+  SimpleCov.start do
+    add_filter '/.bundle/'
+    add_filter '/doc/'
+    add_filter '/spec/'
+    add_filter '/config/'
+    merge_timeout 600
+  end
+end
+
+require 'minitest/autorun'
+
+$:.unshift File.expand_path('../../lib', __FILE__)
+require 'user_agent_parser'
+
+module MiniTest
+  module Assertions
+    # Asserts the test case property is equal to the expected value. On failure
+    # the message includes the property and user_agent_string from the test
+    # case for easier debugging
+    def assert_test_case_property_equal(test_case, actual, test_case_property)
+      assert_equal test_case[test_case_property],
+                   actual,
+                   "#{test_case_property} failed for user agent: #{test_case['user_agent_string']}"
+    end
+
+    Object.infect_an_assertion :assert_test_case_property_equal, :must_equal_test_case_property
+  end
+end
diff --git a/spec/user_agent_spec.rb b/spec/user_agent_spec.rb
new file mode 100644
index 0000000..1944737
--- /dev/null
+++ b/spec/user_agent_spec.rb
@@ -0,0 +1,182 @@
+# frozen_string_literal: true
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+describe UserAgentParser::UserAgent do
+  describe '#to_s' do
+    it 'returns a string of just the family' do
+      _(UserAgentParser::UserAgent.new('Chrome').to_s).must_equal 'Chrome'
+    end
+
+    it 'returns a string of family and version' do
+      version = UserAgentParser::Version.new('1.2.3pre')
+      agent = UserAgentParser::UserAgent.new('Chrome', version)
+      _(agent.to_s).must_equal 'Chrome 1.2.3pre'
+    end
+  end
+
+  describe '#initialize' do
+    describe 'with family' do
+      it 'sets family' do
+        agent = UserAgentParser::UserAgent.new('Chromium')
+        _(agent.family).must_equal 'Chromium'
+      end
+    end
+
+    describe 'with no family' do
+      it 'sets family to Other' do
+        agent = UserAgentParser::UserAgent.new
+        _(agent.family).must_equal 'Other'
+      end
+    end
+
+    describe 'with version' do
+      it 'sets version' do
+        version = UserAgentParser::Version.new('1.2.3')
+        agent = UserAgentParser::UserAgent.new(nil, version)
+        _(agent.version).must_equal version
+      end
+    end
+
+    describe 'with os' do
+      it 'sets os' do
+        os = UserAgentParser::OperatingSystem.new('Windows XP')
+        agent = UserAgentParser::UserAgent.new(nil, nil, os)
+        _(agent.os).must_equal os
+      end
+    end
+
+    describe 'with device' do
+      it 'sets device' do
+        device = UserAgentParser::Device.new('iPhone')
+        agent = UserAgentParser::UserAgent.new(nil, nil, nil, device)
+        _(agent.device).must_equal device
+      end
+    end
+  end
+
+  describe '#name' do
+    it 'returns family' do
+      agent = UserAgentParser::UserAgent.new('Safari')
+      _(agent.name).must_equal agent.family
+    end
+  end
+
+  describe '#==' do
+    it 'returns true for same agents with no OS' do
+      version = UserAgentParser::Version.new('1.0')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', version)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', version)
+      _(agent1).must_equal agent2
+    end
+
+    it 'returns true for same agents on same OS' do
+      version = UserAgentParser::Version.new('1.0')
+      os = UserAgentParser::OperatingSystem.new('Windows')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', version, os)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', version, os)
+      _(agent1).must_equal agent2
+    end
+
+    it 'returns false for same agent on different OS' do
+      version = UserAgentParser::Version.new('1.0')
+      windows = UserAgentParser::OperatingSystem.new('Windows')
+      mac = UserAgentParser::OperatingSystem.new('Mac')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', version, windows)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', version, mac)
+      _(agent1).wont_equal agent2
+    end
+
+    it 'returns false for same os, but different browser version' do
+      browser_version1 = UserAgentParser::Version.new('1.0')
+      browser_version2 = UserAgentParser::Version.new('2.0')
+      os = UserAgentParser::OperatingSystem.new('Windows')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', browser_version1, os)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', browser_version2, os)
+      _(agent1).wont_equal agent2
+    end
+  end
+
+  describe '#eql?' do
+    it 'returns true for same agents with no OS' do
+      version = UserAgentParser::Version.new('1.0')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', version)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', version)
+      assert_equal true, agent1.eql?(agent2)
+    end
+
+    it 'returns true for same agents on same OS' do
+      version = UserAgentParser::Version.new('1.0')
+      os = UserAgentParser::OperatingSystem.new('Windows')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', version, os)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', version, os)
+      assert_equal true, agent1.eql?(agent2)
+    end
+
+    it 'returns false for same agent on different OS' do
+      version = UserAgentParser::Version.new('1.0')
+      windows = UserAgentParser::OperatingSystem.new('Windows')
+      mac = UserAgentParser::OperatingSystem.new('Mac')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', version, windows)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', version, mac)
+      assert_equal false, agent1.eql?(agent2)
+    end
+
+    it 'returns false for same os, but different browser version' do
+      browser_version1 = UserAgentParser::Version.new('1.0')
+      browser_version2 = UserAgentParser::Version.new('2.0')
+      os = UserAgentParser::OperatingSystem.new('Windows')
+      agent1 = UserAgentParser::UserAgent.new('Chrome', browser_version1, os)
+      agent2 = UserAgentParser::UserAgent.new('Chrome', browser_version2, os)
+      assert_equal false, agent1.eql?(agent2)
+    end
+  end
+
+  describe '#inspect' do
+    it 'returns the family and version' do
+      browser_version = UserAgentParser::Version.new('1.0')
+      agent = UserAgentParser::UserAgent.new('Chrome', browser_version)
+      _(agent.inspect.to_s).must_equal '#<UserAgentParser::UserAgent Chrome 1.0>'
+    end
+
+    it 'returns the OS if present' do
+      browser_version = UserAgentParser::Version.new('1.0')
+      os_version = UserAgentParser::Version.new('10.7.4')
+      os = UserAgentParser::OperatingSystem.new('OS X', os_version)
+      agent = UserAgentParser::UserAgent.new('Chrome', browser_version, os)
+      _(agent.inspect).must_equal '#<UserAgentParser::UserAgent Chrome 1.0 (OS X 10.7.4)>'
+    end
+
+    it 'returns device if present' do
+      browser_version = UserAgentParser::Version.new('5.0.2')
+      os_version = UserAgentParser::Version.new('4.2.1')
+      os = UserAgentParser::OperatingSystem.new('iOS', os_version)
+      device = UserAgentParser::Device.new('iPhone')
+      agent = UserAgentParser::UserAgent.new('Mobile Safari', browser_version, os, device)
+      _(agent.inspect).must_equal '#<UserAgentParser::UserAgent Mobile Safari 5.0.2 (iOS 4.2.1) (iPhone)>'
+    end
+  end
+
+  describe '#to_h' do
+    let(:expected) do
+      {
+        device: { family: 'iPhone', model: 'iPhone', brand: nil },
+        family: 'Mobile Safari',
+        os: {
+          version: { version: '4.2.1', major: '4', minor: '2', patch: '1', patch_minor: nil},
+          family: 'iOS'
+        },
+        version: { version: '5.0.2', major: '5', minor: '0', patch: '2', patch_minor: nil }
+      }
+    end
+
+    it 'returns everything' do
+      browser_version = UserAgentParser::Version.new('5.0.2')
+      os_version = UserAgentParser::Version.new('4.2.1')
+      os = UserAgentParser::OperatingSystem.new('iOS', os_version)
+      device = UserAgentParser::Device.new('iPhone')
+      agent = UserAgentParser::UserAgent.new('Mobile Safari', browser_version, os, device)
+      assert_equal(expected, agent.to_h)
+    end
+  end
+end
diff --git a/spec/version_spec.rb b/spec/version_spec.rb
new file mode 100644
index 0000000..ba52866
--- /dev/null
+++ b/spec/version_spec.rb
@@ -0,0 +1,165 @@
+# frozen_string_literal: true
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+describe UserAgentParser::Version do
+  it "parses '1'" do
+    version = UserAgentParser::Version.new('1')
+    _(version.major).must_equal '1'
+  end
+
+  it "parses '1.2'" do
+    version = UserAgentParser::Version.new('1.2')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2'
+  end
+
+  it "parses '1.2.3'" do
+    version = UserAgentParser::Version.new('1.2.3')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2'
+    _(version.patch).must_equal '3'
+  end
+
+  it "parses '1.2.3b4'" do
+    version = UserAgentParser::Version.new('1.2.3b4')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2'
+    _(version.patch).must_equal '3'
+    _(version.patch_minor).must_equal 'b4'
+  end
+
+  it "parses '1.2.3-b4'" do
+    version = UserAgentParser::Version.new('1.2.3-b4')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2'
+    _(version.patch).must_equal '3'
+    _(version.patch_minor).must_equal 'b4'
+  end
+
+  it "parses '1.2.3pre'" do
+    version = UserAgentParser::Version.new('1.2.3pre')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2'
+    _(version.patch).must_equal '3pre'
+  end
+
+  it "parses '1.2.3-45'" do
+    version = UserAgentParser::Version.new('1.2.3-45')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2'
+    _(version.patch).must_equal '3-45'
+  end
+
+  it 'accepts Fixnum and String arguments' do
+    version = UserAgentParser::Version.new(1, '2a', 3, '4b')
+    _(version.major).must_equal '1'
+    _(version.minor).must_equal '2a'
+    _(version.patch).must_equal '3'
+    _(version.patch_minor).must_equal '4b'
+  end
+
+  describe '#to_s' do
+    it 'returns the same string as initialized with' do
+      version = UserAgentParser::Version.new('1.2.3b4')
+      _(version.to_s).must_equal '1.2.3b4'
+    end
+  end
+
+  describe '#==' do
+    it 'returns true for same versions' do
+      version = UserAgentParser::Version.new('1.2.3')
+      _(version).must_equal UserAgentParser::Version.new('1.2.3')
+    end
+
+    it 'returns false for different versions' do
+      version = UserAgentParser::Version.new('1.2.3')
+      _(version).wont_equal UserAgentParser::Version.new('1.2.2')
+    end
+  end
+
+  describe '#<=>' do
+    it 'accepts string for comparison' do
+      version = UserAgentParser::Version.new('1.2.3')
+
+      assert_operator version, :<, '1.2.4'
+      assert_operator version, :==, '1.2.3'
+      assert_operator version, :>, '1.2.2'
+    end
+
+    it 'accepts another instance of Version for comparison' do
+      version = UserAgentParser::Version.new('1.2.3')
+
+      assert_operator version, :>, UserAgentParser::Version.new('1.2.2')
+      assert_operator version, :==, UserAgentParser::Version.new('1.2.3')
+      assert_operator version, :<, UserAgentParser::Version.new('1.2.4')
+    end
+
+    it 'is comparing major version' do
+      version = UserAgentParser::Version.new('1.2.3')
+
+      assert_operator version, :<, '2'
+      assert_operator version, :>=, '1'
+      assert_operator version, :>, '0'
+    end
+
+    it 'is comparing minor version' do
+      version = UserAgentParser::Version.new('1.2.3')
+
+      assert_operator version, :<, '2.0'
+      assert_operator version, :<, '1.3'
+      assert_operator version, :>=, '1.2'
+      assert_operator version, :>, '1.1'
+      assert_operator version, :>, '0.1'
+    end
+
+    it 'is comparing patch level' do
+      version = UserAgentParser::Version.new('1.2.3')
+
+      assert_operator version, :<, '1.2.4'
+      assert_operator version, :>=, '1.2.3'
+      assert_operator version, :<=, '1.2.3'
+      assert_operator version, :>, '1.2.2'
+    end
+
+    it 'is comparing patch_minor level correctly' do
+      version = UserAgentParser::Version.new('1.2.3.p1')
+
+      assert_operator version, :<, '1.2.4'
+      assert_operator version, :<, '1.2.3.p2'
+      assert_operator version, :>=, '1.2.3.p1'
+      assert_operator version, :<=, '1.2.3.p1'
+      assert_operator version, :>, '1.2.3.p0'
+      assert_operator version, :>, '1.2.2'
+      assert_operator version, :>, '1.1'
+    end
+
+    it 'is correctly comparing versions with different lengths' do
+      version = UserAgentParser::Version.new('1.42.3')
+
+      assert_operator version, :<, '1.142'
+      assert_operator version, :<, '1.42.4'
+      assert_operator version, :>=, '1.42'
+      assert_operator version, :>, '1.14'
+      assert_operator version, :>, '1.7'
+      assert_operator version, :>, '1.3'
+    end
+
+    it 'does its best to compare string versions' do
+      version = UserAgentParser::Version.new('1.2.3.a')
+
+      assert_operator version, :<, '1.2.4'
+      assert_operator version, :<, '1.2.3.b'
+      assert_operator version, :<, '1.2.3.p1'
+      assert_operator version, :<, '1.2.3.p0'
+      assert_operator version, :>, '1.2.2'
+    end
+  end
+
+  describe '#inspect' do
+    it 'returns the class and version' do
+      version = UserAgentParser::Version.new('1.2.3')
+      _(version.inspect).must_equal '#<UserAgentParser::Version 1.2.3>'
+    end
+  end
+end
diff --git a/user_agent_parser.gemspec b/user_agent_parser.gemspec
index 86b67f2..bf64195 100644
--- a/user_agent_parser.gemspec
+++ b/user_agent_parser.gemspec
@@ -1,24 +1,21 @@
-#########################################################
-# This file has been automatically generated by gem2tgz #
-#########################################################
-# -*- encoding: utf-8 -*-
-# stub: user_agent_parser 2.5.1 ruby lib
+# frozen_string_literal: true
 
-Gem::Specification.new do |s|
-  s.name = "user_agent_parser".freeze
-  s.version = "2.5.1"
+Gem::Specification.new do |gem|
+  gem.name    = 'user_agent_parser'
+  gem.version = '2.15.0'
 
-  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
-  s.require_paths = ["lib".freeze]
-  s.authors = ["Tim Lucas".freeze]
-  s.date = "2019-01-08"
-  s.description = "A simple, comprehensive Ruby gem for parsing user agent strings with the help of BrowserScope's UA database".freeze
-  s.email = "t@toolmantim.com".freeze
-  s.executables = ["user_agent_parser".freeze]
-  s.files = ["MIT-LICENSE".freeze, "bin/user_agent_parser".freeze, "lib/user_agent_parser.rb".freeze, "lib/user_agent_parser/cli.rb".freeze, "lib/user_agent_parser/device.rb".freeze, "lib/user_agent_parser/operating_system.rb".freeze, "lib/user_agent_parser/parser.rb".freeze, "lib/user_agent_parser/user_agent.rb".freeze, "lib/user_agent_parser/version.rb".freeze, "vendor/uap-core/regexes.yaml".freeze]
-  s.homepage = "https://github.com/ua-parser/uap-ruby".freeze
-  s.licenses = ["MIT".freeze]
-  s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze)
-  s.rubygems_version = "2.7.6.2".freeze
-  s.summary = "A simple, comprehensive Ruby gem for parsing user agent strings with the help of BrowserScope's UA database".freeze
+  gem.authors     = 'Tim Lucas'
+  gem.email       = 't@toolmantim.com'
+  gem.homepage    = 'https://github.com/ua-parser/uap-ruby'
+  gem.summary     = "Parsing user agent strings with the help of BrowserScope's UA database"
+  gem.description = <<~DESCRIPTION
+    A simple, comprehensive Ruby gem for parsing user agent strings
+    with the help of BrowserScope's UserAgent database
+  DESCRIPTION
+  gem.license     = 'MIT'
+  gem.executables = ['user_agent_parser']
+
+  gem.files = %x{git ls-files}.split("\n").select { |d| d =~ %r{^(MIT-LICENSE|Readme.md|lib|bin/)} } + ['vendor/uap-core/regexes.yaml']
+
+  gem.required_ruby_version = '>= 2.5'
 end
diff --git a/vendor/uap-core/regexes.yaml b/vendor/uap-core/regexes.yaml
deleted file mode 100644
index ca12277..0000000
--- a/vendor/uap-core/regexes.yaml
+++ /dev/null
@@ -1,5267 +0,0 @@
-user_agent_parsers:
-  #### SPECIAL CASES TOP ####
-
-  # CFNetwork Podcast catcher Applications
-  - regex: '(ESPN)[%20| ]+Radio/(\d+)\.(\d+)\.(\d+) CFNetwork'
-  - regex: '(Antenna)/(\d+) CFNetwork'
-    family_replacement: 'AntennaPod'
-  - regex: '(TopPodcasts)Pro/(\d+) CFNetwork'
-  - regex: '(MusicDownloader)Lite/(\d+)\.(\d+)\.(\d+) CFNetwork'
-  - regex: '^(.*)-iPad/(\d+)\.?(\d+)?.?(\d+)?.?(\d+)? CFNetwork'
-  - regex: '^(.*)-iPhone/(\d+)\.?(\d+)?.?(\d+)?.?(\d+)? CFNetwork'
-  - regex: '^(.*)/(\d+)\.?(\d+)?.?(\d+)?.?(\d+)? CFNetwork'
-
-  # Podcast catchers
-  - regex: '(espn\.go)'
-    family_replacement: 'ESPN'
-  - regex: '(espnradio\.com)'
-    family_replacement: 'ESPN'
-  - regex: 'ESPN APP$'
-    family_replacement: 'ESPN'
-  - regex: '(audioboom\.com)'
-    family_replacement: 'AudioBoom'
-  - regex: ' (Rivo) RHYTHM'
-
-  # @note: iOS / OSX Applications
-  - regex: '(CFNetwork)(?:/(\d+)\.(\d+)\.?(\d+)?)?'
-    family_replacement: 'CFNetwork'
-
-  # Pingdom
-  - regex: '(Pingdom.com_bot_version_)(\d+)\.(\d+)'
-    family_replacement: 'PingdomBot'
-  # 'Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PingdomTMS/0.8.5 Safari/534.34'
-  - regex: '(PingdomTMS)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'PingdomBot'
-
-  # New Relic Pinger
-  - regex: '(NewRelicPinger)/(\d+)\.(\d+)'
-    family_replacement: 'NewRelicPingerBot'
-
-  # Tableau
-  - regex: '(Tableau)/(\d+)\.(\d+)'
-    family_replacement: 'Tableau'
-
-  # Salesforce
-  - regex: '(Salesforce)(?:.)\/(\d+)\.(\d?)'
-
-  #StatusCake
-  - regex: '(\(StatusCake\))'
-    family_replacement: 'StatusCakeBot'
-
-  # Facebook
-  - regex: '(facebookexternalhit)/(\d+)\.(\d+)'
-    family_replacement: 'FacebookBot'
-
-  # Google Plus
-  - regex: 'Google.*/\+/web/snippet'
-    family_replacement: 'GooglePlusBot'
-
-  # Gmail
-  - regex: 'via ggpht.com GoogleImageProxy'
-    family_replacement: 'GmailImageProxy'
-
-  # Yahoo
-  - regex: 'YahooMailProxy; https://help.yahoo.com/kb/yahoo-mail-proxy-SLN28749.html'
-    family_replacement: 'YahooMailProxy'
-
-  # Twitter
-  - regex: '(Twitterbot)/(\d+)\.(\d+)'
-    family_replacement: 'TwitterBot'
-
-  # Bots Pattern '/name-0.0'
-  - regex: '/((?:Ant-)?Nutch|[A-z]+[Bb]ot|[A-z]+[Ss]pider|Axtaris|fetchurl|Isara|ShopSalad|Tailsweep)[ \-](\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-  # Bots Pattern 'name/0.0'
-  - regex: '\b(008|Altresium|Argus|BaiduMobaider|BoardReader|DNSGroup|DataparkSearch|EDI|Goodzer|Grub|INGRID|Infohelfer|LinkedInBot|LOOQ|Nutch|PathDefender|Peew|PostPost|Steeler|Twitterbot|VSE|WebCrunch|WebZIP|Y!J-BR[A-Z]|YahooSeeker|envolk|sproose|wminer)/(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-
-  # MSIECrawler
-  - regex: '(MSIE) (\d+)\.(\d+)([a-z]\d?)?;.* MSIECrawler'
-    family_replacement: 'MSIECrawler'
-
-  # DAVdroid
-  - regex: '(DAVdroid)/(\d+)\.(\d+)(?:\.(\d+))?'
-
-  # Downloader ...
-  - regex: '(Google-HTTP-Java-Client|Apache-HttpClient|Go-http-client|scalaj-http|http%20client|Python-urllib|HttpMonitor|TLSProber|WinHTTP|JNLP|okhttp|aihttp|reqwest)(?:[ /](\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
-
-  # Pinterestbot
-  - regex: '(Pinterest(?:bot)?)/(\d+)(?:\.(\d+)(?:\.(\d+))?)?[;\s\(]+\+https://www.pinterest.com/bot.html'
-    family_replacement: 'Pinterestbot'
-
-  # Bots
-  - regex: '(1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]+-Agent|AdsBot-Google(?:-[a-z]+)?|altavista|AppEngine-Google|archive.*?\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]+)*|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader(?: [A-Za-z]+)*|boitho.com-dc|BotSeer|BUbiNG|\b\w*favicon\w*\b|\bYeti(?:-[a-z]+)?|Catchpoint(?: bot)?|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher)?|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]+-)?Googlebot(?:-[a-zA-Z]+)?|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile)?|IconSurf|IlTrovatore(?:-Setaccio)?|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]+Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .*? Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media *)?|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]*|^NING|Nutch[^/]*|Nymesis|ObjectsSearch|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|SemrushBot|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|TwitterBot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]+|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s)? Link Sleuth|Xerka [A-z]+Bot|yacy(?:bot)?|Yahoo[a-z]*Seeker|Yahoo! Slurp|Yandex\w+|YodaoBot(?:-[A-z]+)?|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg)(?:[ /]v?(\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
-
-  # AWS S3 Clients
-  # must come before "Bots General matcher" to catch "boto"/"boto3" before "bot"
-  - regex: '\b(Boto3?|JetS3t|aws-(?:cli|sdk-(?:cpp|go|java|nodejs|ruby2?))|s3fs)/(\d+)\.(\d+)(?:\.(\d+))?'
-
-  # Bots General matcher 'name/0.0'
-  - regex: '(?:\/[A-Za-z0-9\.]+)? *([A-Za-z0-9 \-_\!\[\]:]*(?:[Aa]rchiver|[Ii]ndexer|[Ss]craper|[Bb]ot|[Ss]pider|[Cc]rawl[a-z]*))/(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-  # Bots General matcher 'name 0.0'
-  - regex: '(?:\/[A-Za-z0-9\.]+)? *([A-Za-z0-9 _\!\[\]:]*(?:[Aa]rchiver|[Ii]ndexer|[Ss]craper|[Bb]ot|[Ss]pider|[Cc]rawl[a-z]*)) (\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-  # Bots containing spider|scrape|bot(but not CUBOT)|Crawl
-  - regex: '((?:[A-z0-9]+|[A-z\-]+ ?)?(?: the )?(?:[Ss][Pp][Ii][Dd][Ee][Rr]|[Ss]crape|[A-Za-z0-9-]*(?:[^C][^Uu])[Bb]ot|[Cc][Rr][Aa][Ww][Ll])[A-z0-9]*)(?:(?:[ /]| v)(\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
-
-  # HbbTV standard defines what features the browser should understand.
-  # but it's like targeting "HTML5 browsers", effective browser support depends on the model
-  # See os_parsers if you want to target a specific TV
-  - regex: '(HbbTV)/(\d+)\.(\d+)\.(\d+) \('
-
-  # must go before Firefox to catch Chimera/SeaMonkey/Camino/Waterfox
-  - regex: '(Chimera|SeaMonkey|Camino|Waterfox)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)?'
-
-  # Social Networks
-  # Facebook
-  - regex: '\[FB.*;(FBAV)/(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-    family_replacement: 'Facebook'
-  # Pinterest
-  - regex: '\[(Pinterest)/[^\]]+\]'
-  - regex: '(Pinterest)(?: for Android(?: Tablet)?)?/(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-  # Instagram app
-  - regex: 'Mozilla.*Mobile.*(Instagram).(\d+)\.(\d+)\.(\d+)'
-  # Flipbaord app
-  - regex: 'Mozilla.*Mobile.*(Flipboard).(\d+)\.(\d+)\.(\d+)'
-  # Flipbaord-briefing app
-  - regex: 'Mozilla.*Mobile.*(Flipboard-Briefing).(\d+)\.(\d+)\.(\d+)'
-  # Onefootball app
-  - regex: 'Mozilla.*Mobile.*(Onefootball)\/Android.(\d+)\.(\d+)\.(\d+)'
-
-  # Basilisk
-  - regex: '(Firefox)/(\d+)\.(\d+) Basilisk/(\d+)'
-    family_replacement: 'Basilisk'
-
-  # Pale Moon
-  - regex: '(PaleMoon)/(\d+)\.(\d+)\.?(\d+)?'
-    family_replacement: 'Pale Moon'
-
-  # Firefox
-  - regex: '(Fennec)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)'
-    family_replacement: 'Firefox Mobile'
-  - regex: '(Fennec)/(\d+)\.(\d+)(pre)'
-    family_replacement: 'Firefox Mobile'
-  - regex: '(Fennec)/(\d+)\.(\d+)'
-    family_replacement: 'Firefox Mobile'
-  - regex: '(?:Mobile|Tablet);.*(Firefox)/(\d+)\.(\d+)'
-    family_replacement: 'Firefox Mobile'
-  - regex: '(Namoroka|Shiretoko|Minefield)/(\d+)\.(\d+)\.(\d+(?:pre)?)'
-    family_replacement: 'Firefox ($1)'
-  - regex: '(Firefox)/(\d+)\.(\d+)(a\d+[a-z]*)'
-    family_replacement: 'Firefox Alpha'
-  - regex: '(Firefox)/(\d+)\.(\d+)(b\d+[a-z]*)'
-    family_replacement: 'Firefox Beta'
-  - regex: '(Firefox)-(?:\d+\.\d+)?/(\d+)\.(\d+)(a\d+[a-z]*)'
-    family_replacement: 'Firefox Alpha'
-  - regex: '(Firefox)-(?:\d+\.\d+)?/(\d+)\.(\d+)(b\d+[a-z]*)'
-    family_replacement: 'Firefox Beta'
-  - regex: '(Namoroka|Shiretoko|Minefield)/(\d+)\.(\d+)([ab]\d+[a-z]*)?'
-    family_replacement: 'Firefox ($1)'
-  - regex: '(Firefox).*Tablet browser (\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'MicroB'
-  - regex: '(MozillaDeveloperPreview)/(\d+)\.(\d+)([ab]\d+[a-z]*)?'
-  - regex: '(FxiOS)/(\d+)\.(\d+)(\.(\d+))?(\.(\d+))?'
-    family_replacement: 'Firefox iOS'
-
-  # e.g.: Flock/2.0b2
-  - regex: '(Flock)/(\d+)\.(\d+)(b\d+?)'
-
-  # RockMelt
-  - regex: '(RockMelt)/(\d+)\.(\d+)\.(\d+)'
-
-  # e.g.: Fennec/0.9pre
-  - regex: '(Navigator)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Netscape'
-
-  - regex: '(Navigator)/(\d+)\.(\d+)([ab]\d+)'
-    family_replacement: 'Netscape'
-
-  - regex: '(Netscape6)/(\d+)\.(\d+)\.?([ab]?\d+)?'
-    family_replacement: 'Netscape'
-
-  - regex: '(MyIBrow)/(\d+)\.(\d+)'
-    family_replacement: 'My Internet Browser'
-
-  # UC Browser
-  # we need check it before opera. In other case case UC Browser detected look like Opera Mini
-  - regex: '(UC? ?Browser|UCWEB|U3)[ /]?(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'UC Browser'
-
-  # Opera will stop at 9.80 and hide the real version in the Version string.
-  # see: http://dev.opera.com/articles/view/opera-ua-string-changes/
-  - regex: '(Opera Tablet).*Version/(\d+)\.(\d+)(?:\.(\d+))?'
-  - regex: '(Opera Mini)(?:/att)?/?(\d+)?(?:\.(\d+))?(?:\.(\d+))?'
-  - regex: '(Opera)/.+Opera Mobi.+Version/(\d+)\.(\d+)'
-    family_replacement: 'Opera Mobile'
-  - regex: '(Opera)/(\d+)\.(\d+).+Opera Mobi'
-    family_replacement: 'Opera Mobile'
-  - regex: 'Opera Mobi.+(Opera)(?:/|\s+)(\d+)\.(\d+)'
-    family_replacement: 'Opera Mobile'
-  - regex: 'Opera Mobi'
-    family_replacement: 'Opera Mobile'
-  - regex: '(Opera)/9.80.*Version/(\d+)\.(\d+)(?:\.(\d+))?'
-
-  # Opera 14 for Android uses a WebKit render engine.
-  - regex: '(?:Mobile Safari).*(OPR)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Opera Mobile'
-
-  # Opera >=15 for Desktop is similar to Chrome but includes an "OPR" Version string.
-  - regex: '(?:Chrome).*(OPR)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Opera'
-
-  # Opera Coast
-  - regex: '(Coast)/(\d+).(\d+).(\d+)'
-    family_replacement: 'Opera Coast'
-
-  # Opera Mini for iOS (from version 8.0.0)
-  - regex: '(OPiOS)/(\d+).(\d+).(\d+)'
-    family_replacement: 'Opera Mini'
-
-  # Opera Neon
-  - regex: 'Chrome/.+( MMS)/(\d+).(\d+).(\d+)'
-    family_replacement: 'Opera Neon'
-
-  # Palm WebOS looks a lot like Safari.
-  - regex: '(hpw|web)OS/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'webOS Browser'
-
-  # LuaKit has no version info.
-  # http://luakit.org/projects/luakit/
-  - regex: '(luakit)'
-    family_replacement: 'LuaKit'
-
-  # Snowshoe
-  - regex: '(Snowshoe)/(\d+)\.(\d+).(\d+)'
-
-  # Lightning (for Thunderbird)
-  # http://www.mozilla.org/projects/calendar/lightning/
-  - regex: 'Gecko/\d+ (Lightning)/(\d+)\.(\d+)\.?((?:[ab]?\d+[a-z]*)|(?:\d*))'
-
-  # Swiftfox
-  - regex: '(Firefox)/(\d+)\.(\d+)\.(\d+(?:pre)?) \(Swiftfox\)'
-    family_replacement: 'Swiftfox'
-  - regex: '(Firefox)/(\d+)\.(\d+)([ab]\d+[a-z]*)? \(Swiftfox\)'
-    family_replacement: 'Swiftfox'
-
-  # Rekonq
-  - regex: '(rekonq)/(\d+)\.(\d+)\.?(\d+)? Safari'
-    family_replacement: 'Rekonq'
-  - regex: 'rekonq'
-    family_replacement: 'Rekonq'
-
-  # Conkeror lowercase/uppercase
-  # http://conkeror.org/
-  - regex: '(conkeror|Conkeror)/(\d+)\.(\d+)\.?(\d+)?'
-    family_replacement: 'Conkeror'
-
-  # catches lower case konqueror
-  - regex: '(konqueror)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Konqueror'
-
-  - regex: '(WeTab)-Browser'
-
-  - regex: '(Comodo_Dragon)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Comodo Dragon'
-
-  - regex: '(Symphony) (\d+).(\d+)'
-
-  - regex: 'PLAYSTATION 3.+WebKit'
-    family_replacement: 'NetFront NX'
-  - regex: 'PLAYSTATION 3'
-    family_replacement: 'NetFront'
-  - regex: '(PlayStation Portable)'
-    family_replacement: 'NetFront'
-  - regex: '(PlayStation Vita)'
-    family_replacement: 'NetFront NX'
-
-  - regex: 'AppleWebKit.+ (NX)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'NetFront NX'
-  - regex: '(Nintendo 3DS)'
-    family_replacement: 'NetFront NX'
-
-  # Amazon Silk, should go before Safari and Chrome Mobile
-  - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'
-    family_replacement: 'Amazon Silk'
-
-  # @ref: http://www.puffinbrowser.com
-  - regex: '(Puffin)/(\d+)\.(\d+)(?:\.(\d+))?'
-
-  # Edge Mobile
-  - regex: 'Windows Phone .*(Edge)/(\d+)\.(\d+)'
-    family_replacement: 'Edge Mobile'
-
-  # Samsung Internet (based on Chrome, but lacking some features)
-  - regex: '(SamsungBrowser)/(\d+)\.(\d+)'
-    family_replacement: 'Samsung Internet'
-
-  # Seznam.cz browser (based on WebKit)
-  - regex: '(SznProhlizec)/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'Seznam prohlížeč'
-
-  # Coc Coc browser, based on Chrome (used in Vietnam)
-  - regex: '(coc_coc_browser)/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'Coc Coc'
-
-  # Baidu Browsers (desktop spoofs chrome & IE, explorer is mobile)
-  - regex: '(baidubrowser)[/\s](\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-    family_replacement: 'Baidu Browser'
-  - regex: '(FlyFlow)/(\d+)\.(\d+)'
-    family_replacement: 'Baidu Explorer'
-
-  # MxBrowser is Maxthon. Must go before Mobile Chrome for Android
-  - regex: '(MxBrowser)/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'Maxthon'
-
-  # Crosswalk must go before Mobile Chrome for Android
-  - regex: '(Crosswalk)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-
-  # Chrome Mobile
-  - regex: 'Version/.+(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Chrome Mobile WebView'
-  - regex: '; wv\).+(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Chrome Mobile WebView'
-  - regex: '(CrMo)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Chrome Mobile'
-  - regex: '(CriOS)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Chrome Mobile iOS'
-  - regex: '(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+) Mobile(?:[ /]|$)'
-    family_replacement: 'Chrome Mobile'
-  - regex: ' Mobile .*(Chrome)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Chrome Mobile'
-
-  # Chrome Frame must come before MSIE.
-  - regex: '(chromeframe)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Chrome Frame'
-
-  # Tizen Browser (second case included in browser/major.minor regex)
-  - regex: '(SLP Browser)/(\d+)\.(\d+)'
-    family_replacement: 'Tizen Browser'
-
-  # Sogou Explorer 2.X
-  - regex: '(SE 2\.X) MetaSr (\d+)\.(\d+)'
-    family_replacement: 'Sogou Explorer'
-
-  # QQ Browsers
-  - regex: '(MQQBrowser/Mini)(?:(\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
-    family_replacement: 'QQ Browser Mini'
-  - regex: '(MQQBrowser)(?:/(\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
-    family_replacement: 'QQ Browser Mobile'
-  - regex: '(QQBrowser)(?:/(\d+)(?:\.(\d+)\.(\d+)(?:\.(\d+))?)?)?'
-    family_replacement: 'QQ Browser'
-
-  # Rackspace Monitoring
-  - regex: '(Rackspace Monitoring)/(\d+)\.(\d+)'
-    family_replacement: 'RackspaceBot'
-
-  # PyAMF
-  - regex: '(PyAMF)/(\d+)\.(\d+)\.(\d+)'
-
-  # Yandex Browser
-  - regex: '(YaBrowser)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Yandex Browser'
-
-  # Mail.ru Amigo/Internet Browser (Chromium-based)
-  - regex: '(Chrome)/(\d+)\.(\d+)\.(\d+).* MRCHROME'
-    family_replacement: 'Mail.ru Chromium Browser'
-
-  # AOL Browser (IE-based)
-  - regex: '(AOL) (\d+)\.(\d+); AOLBuild (\d+)'
-
-  # Podcast catcher Applications using iTunes
-  - regex: '(PodCruncher|Downcast)[ /]?(\d+)\.?(\d+)?\.?(\d+)?\.?(\d+)?'
-
-  # Box Notes https://www.box.com/resources/downloads
-  # Must be before Electron
-  - regex: ' (BoxNotes)/(\d+)\.(\d+)\.(\d+)'
-
-  # Whale
-  - regex: '(Whale)/(\d+)\.(\d+)\.(\d+)\.(\d+) Mobile(?:[ /]|$)'
-    family_replacement: 'Whale'
-
-  - regex: '(Whale)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Whale'
-
-  #### END SPECIAL CASES TOP ####
-
-  #### MAIN CASES - this catches > 50% of all browsers ####
-
-
-  # Slack desktop client (needs to be before Apple Mail, Electron, and Chrome as it gets wrongly detected on Mac OS otherwise)
-  - regex: '(Slack_SSB)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Slack Desktop Client'
-
-  # HipChat provides a version on Mac, but not on Windows.
-  # Needs to be before Chrome on Windows, and AppleMail on Mac.
-  - regex: '(HipChat)/?(\d+)?'
-    family_replacement: 'HipChat Desktop Client'
-
-  # Browser/major_version.minor_version.beta_version
-  - regex: '\b(MobileIron|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Spotify|Flock|Netscape|Lunascape|WebPilot|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave|MacOutlook|Electron|OktaMobile)/(\d+)\.(\d+)\.(\d+)'
-
-  # Outlook 2007
-  - regex: 'Microsoft Office Outlook 12\.\d+\.\d+|MSOffice 12'
-    family_replacement: 'Outlook'
-    v1_replacement: '2007'
-
-  # Outlook 2010
-  - regex: 'Microsoft Outlook 14\.\d+\.\d+|MSOffice 14'
-    family_replacement: 'Outlook'
-    v1_replacement: '2010'
-
-  # Outlook 2013
-  - regex: 'Microsoft Outlook 15\.\d+\.\d+'
-    family_replacement: 'Outlook'
-    v1_replacement: '2013'
-
-  # Outlook 2016
-  - regex: 'Microsoft Outlook (?:Mail )?16\.\d+\.\d+'
-    family_replacement: 'Outlook'
-    v1_replacement: '2016'
-
-  # Windows Live Mail
-  - regex: 'Outlook-Express\/7\.0.*'
-    family_replacement: 'Windows Live Mail'
-
-  # Apple Air Mail
-  - regex: '(Airmail) (\d+)\.(\d+)(?:\.(\d+))?'
-
-  # Thunderbird
-  - regex: '(Thunderbird)/(\d+)\.(\d+)(?:\.(\d+(?:pre)?))?'
-    family_replacement: 'Thunderbird'
-
-  # Postbox
-  - regex: '(Postbox)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Postbox'
-
-  # Barca
-  - regex: '(Barca(?:Pro)?)/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'Barca'
-
-  # Lotus Notes
-  - regex: '(Lotus-Notes)/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'Lotus Notes'
-
-  # Vivaldi uses "Vivaldi"
-  - regex: '(Vivaldi)/(\d+)\.(\d+)\.(\d+)'
-
-  # Edge/major_version.minor_version
-  - regex: '(Edge)/(\d+)(?:\.(\d+))?'
-
-  # Brave Browser https://brave.com/
-  - regex: '(brave)/(\d+)\.(\d+)\.(\d+) Chrome'
-    family_replacement: 'Brave'
-
-  # Iron Browser ~since version 50
-  - regex: '(Chrome)/(\d+)\.(\d+)\.(\d+)[\d.]* Iron[^/]'
-    family_replacement: 'Iron'
-
-  # Dolphin Browser
-  # @ref: http://www.dolphin.com
-  - regex: '\b(Dolphin)(?: |HDCN/|/INT\-)(\d+)\.(\d+)\.?(\d+)?'
-
-  # Headless Chrome
-  # https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
-  - regex: '(HeadlessChrome)(?:/(\d+)\.(\d+)\.(\d+))?'
-
-  # Evolution Mail CardDav/CalDav integration
-  - regex: '(Evolution)/(\d+)\.(\d+)\.(\d+\.\d+)'
-
-  # Roundcube Mail CardDav plugin
-  - regex: '(RCM CardDAV plugin)/(\d+)\.(\d+)\.(\d+(?:-dev)?)'
-
-  # Browser/major_version.minor_version
-  - regex: '(bingbot|Bolt|AdobeAIR|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|iTunes|MacAppStore|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser|mDolphin|qutebrowser|Otter|QupZilla|MailBar|kmail2|YahooMobileMail|ExchangeWebServices|ExchangeServicesClient|Dragon|Outlook-iOS-Android)/(\d+)\.(\d+)(?:\.(\d+))?'
-
-  # Chrome/Chromium/major_version.minor_version
-  - regex: '(Chromium|Chrome)/(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?'
-
-  ##########
-  # IE Mobile needs to happen before Android to catch cases such as:
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920)...
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920; ANZ821)...
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920; Orange)...
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920; Vodafone)...
-  ##########
-
-  # IE Mobile
-  - regex: '(IEMobile)[ /](\d+)\.(\d+)'
-    family_replacement: 'IE Mobile'
-
-  # Baca Berita App News Reader
-  - regex: '(BacaBerita App)\/(\d+)\.(\d+)\.(\d+)'
-
-  # Podcast catchers
-  - regex: '^(bPod|Pocket Casts|Player FM)$'
-  - regex: '^(AlexaMediaPlayer|VLC)/(\d+)\.(\d+)\.([^.\s]+)'
-  - regex: '^(AntennaPod|WMPlayer|Zune|Podkicker|Radio|ExoPlayerDemo|Overcast|PocketTunes|NSPlayer|okhttp|DoggCatcher|QuickNews|QuickTime|Peapod|Podcasts|GoldenPod|VLC|Spotify|Miro|MediaGo|Juice|iPodder|gPodder|Banshee)/(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?'
-  - regex: '^(Peapod|Liferea)/([^.\s]+)\.([^.\s]+)?\.?([^.\s]+)?'
-  - regex: '^(bPod|Player FM) BMID/(\S+)'
-  - regex: '^(Podcast ?Addict)/v(\d+) '
-  - regex: '^(Podcast ?Addict) '
-    family_replacement: 'PodcastAddict'
-  - regex: '(Replay) AV'
-  - regex: '(VOX) Music Player'
-  - regex: '(CITA) RSS Aggregator/(\d+)\.(\d+)'
-  - regex: '(Pocket Casts)$'
-  - regex: '(Player FM)$'
-  - regex: '(LG Player|Doppler|FancyMusic|MediaMonkey|Clementine) (\d+)\.(\d+)\.?([^.\s]+)?\.?([^.\s]+)?'
-  - regex: '(philpodder)/(\d+)\.(\d+)\.?([^.\s]+)?\.?([^.\s]+)?'
-  - regex: '(Player FM|Pocket Casts|DoggCatcher|Spotify|MediaMonkey|MediaGo|BashPodder)'
-  - regex: '(QuickTime)\.(\d+)\.(\d+)\.(\d+)'
-  - regex: '(Kinoma)(\d+)'
-  - regex: '(Fancy) Cloud Music (\d+)\.(\d+)'
-    family_replacement: 'FancyMusic'
-  - regex: 'EspnDownloadManager'
-    family_replacement: 'ESPN'
-  - regex: '(ESPN) Radio (\d+)\.(\d+)\.?(\d+)? ?(?:rv:(\d+))? '
-  - regex: '(podracer|jPodder) v ?(\d+)\.(\d+)\.?(\d+)?'
-  - regex: '(ZDM)/(\d+)\.(\d+)[; ]?'
-  - regex: '(Zune|BeyondPod) (\d+)\.?(\d+)?[\);]'
-  - regex: '(WMPlayer)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-  - regex: '^(Lavf)'
-    family_replacement: 'WMPlayer'
-  - regex: '^(RSSRadio)[ /]?(\d+)?'
-  - regex: '(RSS_Radio) (\d+)\.(\d+)'
-    family_replacement: 'RSSRadio'
-  - regex: '(Podkicker) \S+/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Podkicker'
-  - regex: '^(HTC) Streaming Player \S+ / \S+ / \S+ / (\d+)\.(\d+)\.?(\d+)?'
-  - regex: '^(Stitcher)/iOS'
-  - regex: '^(Stitcher)/Android'
-  - regex: '^(VLC) .*version (\d+)\.(\d+)\.(\d+)'
-  - regex: ' (VLC) for'
-  - regex: '(vlc)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'VLC'
-  - regex: '^(foobar)\S+/([^.\s]+)\.([^.\s]+)?\.?([^.\s]+)?'
-  - regex: '^(Clementine)\S+ ([^.\s]+)\.([^.\s]+)?\.?([^.\s]+)?'
-  - regex: '(amarok)/([^.\s]+)\.([^.\s]+)?\.?([^.\s]+)?'
-    family_replacement: 'Amarok'
-  - regex: '(Custom)-Feed Reader'
-
-  # Browser major_version.minor_version.beta_version (space instead of slash)
-  - regex: '(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) (\d+)\.(\d+)\.(\d+)'
-  # Browser major_version.minor_version (space instead of slash)
-  - regex: '(iCab|Lunascape|Opera|Android|Jasmine|Polaris|Microsoft SkyDriveSync|The Bat!) (\d+)\.(\d+)\.?(\d+)?'
-
-  # Kindle WebKit
-  - regex: '(Kindle)/(\d+)\.(\d+)'
-
-  # weird android UAs
-  - regex: '(Android) Donut'
-    v1_replacement: '1'
-    v2_replacement: '2'
-
-  - regex: '(Android) Eclair'
-    v1_replacement: '2'
-    v2_replacement: '1'
-
-  - regex: '(Android) Froyo'
-    v1_replacement: '2'
-    v2_replacement: '2'
-
-  - regex: '(Android) Gingerbread'
-    v1_replacement: '2'
-    v2_replacement: '3'
-
-  - regex: '(Android) Honeycomb'
-    v1_replacement: '3'
-
-  # desktop mode
-  # http://www.anandtech.com/show/3982/windows-phone-7-review
-  - regex: '(MSIE) (\d+)\.(\d+).*XBLWP7'
-    family_replacement: 'IE Large Screen'
-
-  # Nextcloud desktop sync client
-  - regex: '(Nextcloud)'
-
-  # Generic mirall client
-  - regex: '(mirall)/(\d+)\.(\d+)\.(\d+)'
-
-  # Nextcloud/Owncloud android client
-  - regex: '(ownCloud-android)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Owncloud'
-
-  #### END MAIN CASES ####
-
-  #### SPECIAL CASES ####
-  - regex: '(Obigo)InternetBrowser'
-  - regex: '(Obigo)\-Browser'
-  - regex: '(Obigo|OBIGO)[^\d]*(\d+)(?:.(\d+))?'
-    family_replacement: 'Obigo'
-
-  - regex: '(MAXTHON|Maxthon) (\d+)\.(\d+)'
-    family_replacement: 'Maxthon'
-  - regex: '(Maxthon|MyIE2|Uzbl|Shiira)'
-    v1_replacement: '0'
-
-  - regex: '(BrowseX) \((\d+)\.(\d+)\.(\d+)'
-
-  - regex: '(NCSA_Mosaic)/(\d+)\.(\d+)'
-    family_replacement: 'NCSA Mosaic'
-
-  # Polaris/d.d is above
-  - regex: '(POLARIS)/(\d+)\.(\d+)'
-    family_replacement: 'Polaris'
-  - regex: '(Embider)/(\d+)\.(\d+)'
-    family_replacement: 'Polaris'
-
-  - regex: '(BonEcho)/(\d+)\.(\d+)\.?([ab]?\d+)?'
-    family_replacement: 'Bon Echo'
-
-  # @note: iOS / OSX Applications
-  - regex: '(iPod|iPhone|iPad).+Version/(\d+)\.(\d+)(?:\.(\d+))?.*[ +]Safari'
-    family_replacement: 'Mobile Safari'
-  - regex: '(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\d+)_(\d+)(?:_(\d+))?.* AppleNews\/\d+\.\d+\.\d+?'
-    family_replacement: 'Mobile Safari UI/WKWebView'
-  - regex: '(iPod|iPhone|iPad).+Version/(\d+)\.(\d+)(?:\.(\d+))?'
-    family_replacement: 'Mobile Safari UI/WKWebView'
-  - regex: '(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\d+)_(\d+)(?:_(\d+))?.*Mobile.*[ +]Safari'
-    family_replacement: 'Mobile Safari'
-  - regex: '(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\d+)_(\d+)(?:_(\d+))?.*Mobile'
-    family_replacement: 'Mobile Safari UI/WKWebView'
-  - regex: '(iPod|iPhone|iPad).* Safari'
-    family_replacement: 'Mobile Safari'
-  - regex: '(iPod|iPhone|iPad)'
-    family_replacement: 'Mobile Safari UI/WKWebView'
-
-  ##########################
-  # Outlook on iOS >= 2.62.0
-  ##########################
-  - regex: '(Outlook-iOS)/\d+\.\d+\.prod\.iphone \((\d+)\.(\d+)\.(\d+)\)'
-
-  - regex: '(AvantGo) (\d+).(\d+)'
-
-  - regex: '(OneBrowser)/(\d+).(\d+)'
-    family_replacement: 'ONE Browser'
-
-  - regex: '(Avant)'
-    v1_replacement: '1'
-
-  # This is the Tesla Model S (see similar entry in device parsers)
-  - regex: '(QtCarBrowser)'
-    v1_replacement: '1'
-
-  - regex: '^(iBrowser/Mini)(\d+).(\d+)'
-    family_replacement: 'iBrowser Mini'
-  - regex: '^(iBrowser|iRAPP)/(\d+).(\d+)'
-
-  # nokia browsers
-  # based on: http://www.developer.nokia.com/Community/Wiki/User-Agent_headers_for_Nokia_devices
-  - regex: '^(Nokia)'
-    family_replacement: 'Nokia Services (WAP) Browser'
-  - regex: '(NokiaBrowser)/(\d+)\.(\d+).(\d+)\.(\d+)'
-    family_replacement: 'Nokia Browser'
-  - regex: '(NokiaBrowser)/(\d+)\.(\d+).(\d+)'
-    family_replacement: 'Nokia Browser'
-  - regex: '(NokiaBrowser)/(\d+)\.(\d+)'
-    family_replacement: 'Nokia Browser'
-  - regex: '(BrowserNG)/(\d+)\.(\d+).(\d+)'
-    family_replacement: 'Nokia Browser'
-  - regex: '(Series60)/5\.0'
-    family_replacement: 'Nokia Browser'
-    v1_replacement: '7'
-    v2_replacement: '0'
-  - regex: '(Series60)/(\d+)\.(\d+)'
-    family_replacement: 'Nokia OSS Browser'
-  - regex: '(S40OviBrowser)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Ovi Browser'
-  - regex: '(Nokia)[EN]?(\d+)'
-
-  # BlackBerry devices
-  - regex: '(PlayBook).+RIM Tablet OS (\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'BlackBerry WebKit'
-  - regex: '(Black[bB]erry|BB10).+Version/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'BlackBerry WebKit'
-  - regex: '(Black[bB]erry)\s?(\d+)'
-    family_replacement: 'BlackBerry'
-
-  - regex: '(OmniWeb)/v(\d+)\.(\d+)'
-
-  - regex: '(Blazer)/(\d+)\.(\d+)'
-    family_replacement: 'Palm Blazer'
-
-  - regex: '(Pre)/(\d+)\.(\d+)'
-    family_replacement: 'Palm Pre'
-
-  # fork of Links
-  - regex: '(ELinks)/(\d+)\.(\d+)'
-  - regex: '(ELinks) \((\d+)\.(\d+)'
-  - regex: '(Links) \((\d+)\.(\d+)'
-
-  - regex: '(QtWeb) Internet Browser/(\d+)\.(\d+)'
-
-  #- regex: '\(iPad;.+(Version)/(\d+)\.(\d+)(?:\.(\d+))?.*Safari/'
-  #  family_replacement: 'iPad'
-
-  # Phantomjs, should go before Safari
-  - regex: '(PhantomJS)/(\d+)\.(\d+)\.(\d+)'
-
-  # WebKit Nightly
-  - regex: '(AppleWebKit)/(\d+)\.?(\d+)?\+ .* Safari'
-    family_replacement: 'WebKit Nightly'
-
-  # Safari
-  - regex: '(Version)/(\d+)\.(\d+)(?:\.(\d+))?.*Safari/'
-    family_replacement: 'Safari'
-  # Safari didn't provide "Version/d.d.d" prior to 3.0
-  - regex: '(Safari)/\d+'
-
-  - regex: '(OLPC)/Update(\d+)\.(\d+)'
-
-  - regex: '(OLPC)/Update()\.(\d+)'
-    v1_replacement: '0'
-
-  - regex: '(SEMC\-Browser)/(\d+)\.(\d+)'
-
-  - regex: '(Teleca)'
-    family_replacement: 'Teleca Browser'
-
-  - regex: '(Phantom)/V(\d+)\.(\d+)'
-    family_replacement: 'Phantom Browser'
-
-  - regex: '(Trident)/(7|8)\.(0)'
-    family_replacement: 'IE'
-    v1_replacement: '11'
-
-  - regex: '(Trident)/(6)\.(0)'
-    family_replacement: 'IE'
-    v1_replacement: '10'
-
-  - regex: '(Trident)/(5)\.(0)'
-    family_replacement: 'IE'
-    v1_replacement: '9'
-
-  - regex: '(Trident)/(4)\.(0)'
-    family_replacement: 'IE'
-    v1_replacement: '8'
-
-  # Espial
-  - regex: '(Espial)/(\d+)(?:\.(\d+))?(?:\.(\d+))?'
-
- # Apple Mail
-
-  # apple mail - not directly detectable, have it after Safari stuff
-  - regex: '(AppleWebKit)/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Apple Mail'
-
-  # AFTER THE EDGE CASES ABOVE!
-  # AFTER IE11
-  # BEFORE all other IE
-  - regex: '(Firefox)/(\d+)\.(\d+)\.(\d+)'
-  - regex: '(Firefox)/(\d+)\.(\d+)(pre|[ab]\d+[a-z]*)?'
-
-  - regex: '([MS]?IE) (\d+)\.(\d+)'
-    family_replacement: 'IE'
-
-  - regex: '(python-requests)/(\d+)\.(\d+)'
-    family_replacement: 'Python Requests'
-
-  # headless user-agents
-  - regex: '\b(Windows-Update-Agent|Microsoft-CryptoAPI|SophosUpdateManager|SophosAgent|Debian APT-HTTP|Ubuntu APT-HTTP|libcurl-agent|libwww-perl|urlgrabber|curl|PycURL|Wget|aria2|Axel|OpenBSD ftp|lftp|jupdate|insomnia)(?:[ /](\d+)(?:\.(\d+)(?:\.(\d+))?)?)?'
-
-  - regex: '(Java)[/ ]{0,1}\d+\.(\d+)\.(\d+)[_-]*([a-zA-Z0-9]+)*'
-
-  # Cloud Storage Clients
-  - regex: '^(Cyberduck)/(\d+)\.(\d+)\.(\d+)(?:\.\d+)?'
-  - regex: '^(S3 Browser) (\d+)-(\d+)-(\d+)(?:\s*http://s3browser\.com)?'
-  # rclone - rsync for cloud storage - https://rclone.org/
-  - regex: '^(rclone)/v(\d+)\.(\d+)'
-
-  # Roku Digital-Video-Players https://www.roku.com/
-  - regex: '^(Roku)/DVP-(\d+)\.(\d+)'
-
-  # Kurio App News Reader https://kurio.co.id/
-  - regex: '(Kurio)\/(\d+)\.(\d+)\.(\d+)'
-    family_replacement: 'Kurio App'
-
-  # Box Drive and Box Sync https://www.box.com/resources/downloads
-  - regex: '^(Box(?: Sync)?)/(\d+)\.(\d+)\.(\d+)'
-
-os_parsers:
-  ##########
-  # HbbTV vendors
-  ##########
-
-  # starts with the easy one : Panasonic seems consistent across years, hope it will continue
-  #HbbTV/1.1.1 (;Panasonic;VIERA 2011;f.532;0071-0802 2000-0000;)
-  #HbbTV/1.1.1 (;Panasonic;VIERA 2012;1.261;0071-3103 2000-0000;)
-  #HbbTV/1.2.1 (;Panasonic;VIERA 2013;3.672;4101-0003 0002-0000;)
-  #- regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Panasonic);VIERA ([0-9]{4});'
-
-  # Sony is consistent too but do not place year like the other
-  # Opera/9.80 (Linux armv7l; HbbTV/1.1.1 (; Sony; KDL32W650A; PKG3.211EUA; 2013;); ) Presto/2.12.362 Version/12.11
-  # Opera/9.80 (Linux mips; U;  HbbTV/1.1.1 (; Sony; KDL40HX751; PKG1.902EUA; 2012;);; en) Presto/2.10.250 Version/11.60
-  # Opera/9.80 (Linux mips; U;  HbbTV/1.1.1 (; Sony; KDL22EX320; PKG4.017EUA; 2011;);; en) Presto/2.7.61 Version/11.00
-  #- regex: 'HbbTV/\d+\.\d+\.\d+ \(; (Sony);.*;.*; ([0-9]{4});\)'
-
-
-  # LG is consistent too, but we need to add manually the year model
-  #Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ HbbTV/1.1.1 ( ;LGE ;NetCast 4.0 ;03.20.30 ;1.0M ;)
-  #Mozilla/5.0 (DirectFB; Linux armv7l) AppleWebKit/534.26+ (KHTML, like Gecko) Version/5.0 Safari/534.26+ HbbTV/1.1.1 ( ;LGE ;NetCast 3.0 ;1.0 ;1.0M ;)
-  - regex: 'HbbTV/\d+\.\d+\.\d+ \( ;(LG)E ;NetCast 4.0'
-    os_v1_replacement: '2013'
-  - regex: 'HbbTV/\d+\.\d+\.\d+ \( ;(LG)E ;NetCast 3.0'
-    os_v1_replacement: '2012'
-
-  # Samsung is on its way of normalizing their user-agent
-  # HbbTV/1.1.1 (;Samsung;SmartTV2013;T-FXPDEUC-1102.2;;) WebKit
-  # HbbTV/1.1.1 (;Samsung;SmartTV2013;T-MST12DEUC-1102.1;;) WebKit
-  # HbbTV/1.1.1 (;Samsung;SmartTV2012;;;) WebKit
-  # HbbTV/1.1.1 (;;;;;) Maple_2011
-  - regex: 'HbbTV/1.1.1 \(;;;;;\) Maple_2011'
-    os_replacement: 'Samsung'
-    os_v1_replacement: '2011'
-  # manage the two models of 2013
-  - regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Samsung);SmartTV([0-9]{4});.*FXPDEUC'
-    os_v2_replacement: 'UE40F7000'
-  - regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Samsung);SmartTV([0-9]{4});.*MST12DEUC'
-    os_v2_replacement: 'UE32F4500'
-  # generic Samsung (works starting in 2012)
-  #- regex: 'HbbTV/\d+\.\d+\.\d+ \(;(Samsung);SmartTV([0-9]{4});'
-
-  # Philips : not found any other way than a manual mapping
-  # Opera/9.80 (Linux mips; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/4.1.3 PHILIPSTV/1.1.1; en) Presto/2.10.250 Version/11.60
-  # Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70
-  - regex: 'HbbTV/1.1.1 \(; (Philips);.*NETTV/4'
-    os_v1_replacement: '2013'
-  - regex: 'HbbTV/1.1.1 \(; (Philips);.*NETTV/3'
-    os_v1_replacement: '2012'
-  - regex: 'HbbTV/1.1.1 \(; (Philips);.*NETTV/2'
-    os_v1_replacement: '2011'
-
-  # the HbbTV emulator developers use HbbTV/1.1.1 (;;;;;) firetv-firefox-plugin 1.1.20
-  - regex: 'HbbTV/\d+\.\d+\.\d+.*(firetv)-firefox-plugin (\d+).(\d+).(\d+)'
-    os_replacement: 'FireHbbTV'
-
-  # generic HbbTV, hoping to catch manufacturer name (always after 2nd comma) and the first string that looks like a 2011-2019 year
-  - regex: 'HbbTV/\d+\.\d+\.\d+ \(.*; ?([a-zA-Z]+) ?;.*(201[1-9]).*\)'
-
-  ##########
-  # @note: Windows Phone needs to come before Windows NT 6.1 *and* before Android to catch cases such as:
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920)...
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920; ANZ821)...
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920; Orange)...
-  # Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920; Vodafone)...
-  ##########
-
-  - regex: '(Windows Phone) (?:OS[ /])?(\d+)\.(\d+)'
-
-  # Again a MS-special one: iPhone.*Outlook-iOS-Android/x.x is erroneously detected as Android
-  - regex: '(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone)[ +]+(\d+)[_\.](\d+)(?:[_\.](\d+))?.*Outlook-iOS-Android'
-    os_replacement: 'iOS'
-
-  ##########
-  # Android
-  # can actually detect rooted android os. do we care?
-  ##########
-  - regex: '(Android)[ \-/](\d+)\.?(\d+)?(?:[.\-]([a-z0-9]+))?'
-
-  - regex: '(Android) Donut'
-    os_v1_replacement: '1'
-    os_v2_replacement: '2'
-
-  - regex: '(Android) Eclair'
-    os_v1_replacement: '2'
-    os_v2_replacement: '1'
-
-  - regex: '(Android) Froyo'
-    os_v1_replacement: '2'
-    os_v2_replacement: '2'
-
-  - regex: '(Android) Gingerbread'
-    os_v1_replacement: '2'
-    os_v2_replacement: '3'
-
-  - regex: '(Android) Honeycomb'
-    os_v1_replacement: '3'
-
-  # UCWEB
-  - regex: '^UCWEB.*; (Adr) (\d+)\.(\d+)(?:[.\-]([a-z0-9]+))?;'
-    os_replacement: 'Android'
-  - regex: '^UCWEB.*; (iPad|iPh|iPd) OS (\d+)_(\d+)(?:_(\d+))?;'
-    os_replacement: 'iOS'
-  - regex: '^UCWEB.*; (wds) (\d+)\.(\d+)(?:\.(\d+))?;'
-    os_replacement: 'Windows Phone'
-  # JUC
-  - regex: '^(JUC).*; ?U; ?(?:Android)?(\d+)\.(\d+)(?:[\.\-]([a-z0-9]+))?'
-    os_replacement: 'Android'
-
-  # Salesforce
-  - regex: '(android)\s(?:mobile\/)(\d+)\.(\d+)\.?(\d+)?'
-    os_replacement: 'Android'
-
-  ##########
-  # Kindle Android
-  ##########
-  - regex: '(Silk-Accelerated=[a-z]{4,5})'
-    os_replacement: 'Android'
-
-  ##########
-  # Windows
-  # http://en.wikipedia.org/wiki/Windows_NT#Releases
-  # possibility of false positive when different marketing names share same NT kernel
-  # e.g. windows server 2003 and windows xp
-  # lots of ua strings have Windows NT 4.1 !?!?!?!? !?!? !? !????!?! !!! ??? !?!?! ?
-  # (very) roughly ordered in terms of frequency of occurence of regex (win xp currently most frequent, etc)
-  ##########
-
-  # ie mobile desktop mode
-  # spoofs nt 6.1. must come before windows 7
-  - regex: '(XBLWP7)'
-    os_replacement: 'Windows Phone'
-
-  # @note: This needs to come before Windows NT 6.1
-  - regex: '(Windows ?Mobile)'
-    os_replacement: 'Windows Mobile'
-
-  - regex: '(Windows (?:NT 5\.2|NT 5\.1))'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'XP'
-
-  - regex: '(Windows NT 6\.1)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '7'
-
-  - regex: '(Windows NT 6\.0)'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'Vista'
-
-  - regex: '(Win 9x 4\.90)'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'ME'
-
-  - regex: '(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\.0|Windows Vista|Windows 2000|Windows 3.1)'
-
-  - regex: '(Windows NT 6\.2; ARM;)'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'RT'
-
-  - regex: '(Windows NT 6\.2)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '8'
-
-  - regex: '(Windows NT 6\.3; ARM;)'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'RT 8.1'
-
-  - regex: '(Windows NT 6\.3)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '8.1'
-
-  - regex: '(Windows NT 6\.4)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '10'
-
-  - regex: '(Windows NT 10\.0)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '10'
-
-  - regex: '(Windows NT 5\.0)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '2000'
-
-  - regex: '(WinNT4.0)'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'NT 4.0'
-
-  - regex: '(Windows ?CE)'
-    os_replacement: 'Windows'
-    os_v1_replacement: 'CE'
-
-  - regex: 'Win ?(95|98|3.1|NT|ME|2000)'
-    os_replacement: 'Windows'
-    os_v1_replacement: '$1'
-
-  - regex: 'Win16'
-    os_replacement: 'Windows'
-    os_v1_replacement: '3.1'
-
-  - regex: 'Win32'
-    os_replacement: 'Windows'
-    os_v1_replacement: '95'
-
-  # Box apps (Drive, Sync, Notes) on Windows https://www.box.com/resources/downloads
-  - regex: '^Box.*Windows/([\d.]+);'
-    os_replacement: 'Windows'
-    os_v1_replacement: '$1'
-
-  ##########
-  # Tizen OS from Samsung
-  # spoofs Android so pushing it above
-  ##########
-  - regex: '(Tizen)[/ ](\d+)\.(\d+)'
-
-  ##########
-  # Mac OS
-  # @ref: http://en.wikipedia.org/wiki/Mac_OS_X#Versions
-  # @ref: http://www.puredarwin.org/curious/versions
-  ##########
-  - regex: '((?:Mac[ +]?|; )OS[ +]X)[\s+/](?:(\d+)[_.](\d+)(?:[_.](\d+))?|Mach-O)'
-    os_replacement: 'Mac OS X'
-  - regex: '(\w+\s+Mac OS X\s+\w+\s+(\d+).(\d+).(\d+).*)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '$2'
-    os_v2_replacement: '$3'
-    os_v3_replacement: '$4'
-  # Leopard
-  - regex: ' (Dar)(win)/(9).(\d+).*\((?:i386|x86_64|Power Macintosh)\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '5'
-  # Snow Leopard
-  - regex: ' (Dar)(win)/(10).(\d+).*\((?:i386|x86_64)\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '6'
-  # Lion
-  - regex: ' (Dar)(win)/(11).(\d+).*\((?:i386|x86_64)\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '7'
-  # Mountain Lion
-  - regex: ' (Dar)(win)/(12).(\d+).*\((?:i386|x86_64)\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '8'
-  # Mavericks
-  - regex: ' (Dar)(win)/(13).(\d+).*\((?:i386|x86_64)\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '9'
-  # Yosemite is Darwin/14.x but patch versions are inconsistent in the Darwin string;
-  # more accurately covered by CFNetwork regexes downstream
-
-  # IE on Mac doesn't specify version number
-  - regex: 'Mac_PowerPC'
-    os_replacement: 'Mac OS'
-
-  # builds before tiger don't seem to specify version?
-
-  # ios devices spoof (mac os x), so including intel/ppc prefixes
-  - regex: '(?:PPC|Intel) (Mac OS X)'
-
-  # Box Drive and Box Sync on Mac OS X use OSX version numbers, not Darwin
-  - regex: '^Box.*;(Darwin)/(10)\.(1\d)(?:\.(\d+))?'
-    os_replacement: 'Mac OS X'
-
-  ##########
-  # iOS
-  # http://en.wikipedia.org/wiki/IOS_version_history
-  ##########
-  # keep this above generic iOS, since AppleTV UAs contain 'CPU OS'
-  - regex: '(Apple\s?TV)(?:/(\d+)\.(\d+))?'
-    os_replacement: 'ATV OS X'
-
-  - regex: '(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(\d+)[_\.](\d+)(?:[_\.](\d+))?'
-    os_replacement: 'iOS'
-
-  # remaining cases are mostly only opera uas, so catch opera as to not catch iphone spoofs
-  - regex: '(iPhone|iPad|iPod); Opera'
-    os_replacement: 'iOS'
-
-  # few more stragglers
-  - regex: '(iPhone|iPad|iPod).*Mac OS X.*Version/(\d+)\.(\d+)'
-    os_replacement: 'iOS'
-
-  # CFNetwork/Darwin - The specific CFNetwork or Darwin version determines
-  # whether the os maps to Mac OS, or iOS, or just Darwin.
-  # See: http://user-agents.me/cfnetwork-version-list
-  - regex: '(CFNetwork)/(5)48\.0\.3.* Darwin/11\.0\.0'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/(5)48\.(0)\.4.* Darwin/(1)1\.0\.0'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/(5)48\.(1)\.4'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/(4)85\.1(3)\.9'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/(6)09\.(1)\.4'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/(6)(0)9'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/6(7)2\.(1)\.13'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/6(7)2\.(1)\.(1)4'
-    os_replacement: 'iOS'
-  - regex: '(CF)(Network)/6(7)(2)\.1\.15'
-    os_replacement: 'iOS'
-    os_v1_replacement: '7'
-    os_v2_replacement: '1'
-  - regex: '(CFNetwork)/6(7)2\.(0)\.(?:2|8)'
-    os_replacement: 'iOS'
-  - regex: '(CFNetwork)/709\.1'
-    os_replacement: 'iOS'
-    os_v1_replacement: '8'
-    os_v2_replacement: '0.b5'
-  - regex: '(CF)(Network)/711\.(\d)'
-    os_replacement: 'iOS'
-    os_v1_replacement: '8'
-  - regex: '(CF)(Network)/(720)\.(\d)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '10'
-  - regex: '(CF)(Network)/(760)\.(\d)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '11'
-  - regex: 'CFNetwork/7.* Darwin/15\.4\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '9'
-    os_v2_replacement: '3'
-    os_v3_replacement: '1'
-  - regex: 'CFNetwork/7.* Darwin/15\.5\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '9'
-    os_v2_replacement: '3'
-    os_v3_replacement: '2'
-  - regex: 'CFNetwork/7.* Darwin/15\.6\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '9'
-    os_v2_replacement: '3'
-    os_v3_replacement: '5'
-  - regex: '(CF)(Network)/758\.(\d)'
-    os_replacement: 'iOS'
-    os_v1_replacement: '9'
-  - regex: 'CFNetwork/808\.3 Darwin/16\.3\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '10'
-    os_v2_replacement: '2'
-    os_v3_replacement: '1'
-  - regex: '(CF)(Network)/808\.(\d)'
-    os_replacement: 'iOS'
-    os_v1_replacement: '10'
-
-  ##########
-  # CFNetwork macOS Apps (must be before CFNetwork iOS Apps
-  # @ref: https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history
-  ##########
-  - regex: 'CFNetwork/.* Darwin/17\.\d+.*\(x86_64\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '13'
-  - regex: 'CFNetwork/.* Darwin/16\.\d+.*\(x86_64\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '12'
-  - regex: 'CFNetwork/8.* Darwin/15\.\d+.*\(x86_64\)'
-    os_replacement: 'Mac OS X'
-    os_v1_replacement: '10'
-    os_v2_replacement: '11'
-  ##########
-  # CFNetwork iOS Apps
-  # @ref: https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history
-  ##########
-  - regex: 'CFNetwork/.* Darwin/(9)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '1'
-  - regex: 'CFNetwork/.* Darwin/(10)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '4'
-  - regex: 'CFNetwork/.* Darwin/(11)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '5'
-  - regex: 'CFNetwork/.* Darwin/(13)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '6'
-  - regex: 'CFNetwork/6.* Darwin/(14)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '7'
-  - regex: 'CFNetwork/7.* Darwin/(14)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '8'
-    os_v2_replacement: '0'
-  - regex: 'CFNetwork/7.* Darwin/(15)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '9'
-    os_v2_replacement: '0'
-  - regex: 'CFNetwork/8.* Darwin/16\.5\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '10'
-    os_v2_replacement: '3'
-  - regex: 'CFNetwork/8.* Darwin/16\.6\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '10'
-    os_v2_replacement: '3'
-    os_v3_replacement: '2'
-  - regex: 'CFNetwork/8.* Darwin/16\.7\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '10'
-    os_v2_replacement: '3'
-    os_v3_replacement: '3'
-  - regex: 'CFNetwork/8.* Darwin/(16)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '10'
-  - regex: 'CFNetwork/8.* Darwin/17\.0\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '0'
-  - regex: 'CFNetwork/8.* Darwin/17\.2\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '1'
-  - regex: 'CFNetwork/8.* Darwin/17\.3\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '2'
-  - regex: 'CFNetwork/8.* Darwin/17\.4\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '2'
-    os_v3_replacement: '6'
-  - regex: 'CFNetwork/8.* Darwin/17\.5\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '3'
-  - regex: 'CFNetwork/9.* Darwin/17\.6\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '4'
-  - regex: 'CFNetwork/9.* Darwin/17\.7\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-    os_v2_replacement: '4'
-    os_v3_replacement: '1'
-  - regex: 'CFNetwork/8.* Darwin/(17)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '11'
-  - regex: 'CFNetwork/9.* Darwin/18\.0\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '12'
-    os_v2_replacement: '0'
-  - regex: 'CFNetwork/9.* Darwin/(18)\.\d+'
-    os_replacement: 'iOS'
-    os_v1_replacement: '12'
-  - regex: 'CFNetwork/.* Darwin/'
-    os_replacement: 'iOS'
-
-  # iOS Apps
-  - regex: '\b(iOS[ /]|iOS; |iPhone(?:/| v|[ _]OS[/,]|; | OS : |\d,\d/|\d,\d; )|iPad/)(\d{1,2})[_\.](\d{1,2})(?:[_\.](\d+))?'
-    os_replacement: 'iOS'
-  - regex: '\((iOS);'
-
-  ##########################
-  # Outlook on iOS >= 2.62.0
-  ##########################
-  - regex: 'Outlook-(iOS)/\d+\.\d+\.prod\.iphone'
-
-  ##########
-  # Apple TV
-  ##########
-  - regex: '(tvOS)/(\d+).(\d+)'
-    os_replacement: 'tvOS'
-
-  ##########
-  # Chrome OS
-  # if version 0.0.0, probably this stuff:
-  # http://code.google.com/p/chromium-os/issues/detail?id=11573
-  # http://code.google.com/p/chromium-os/issues/detail?id=13790
-  ##########
-  - regex: '(CrOS) [a-z0-9_]+ (\d+)\.(\d+)(?:\.(\d+))?'
-    os_replacement: 'Chrome OS'
-
-  ##########
-  # Linux distros
-  ##########
-  - regex: '([Dd]ebian)'
-    os_replacement: 'Debian'
-  - regex: '(Linux Mint)(?:/(\d+))?'
-  - regex: '(Mandriva)(?: Linux)?/(?:[\d.-]+m[a-z]{2}(\d+).(\d))?'
-
-  ##########
-  # Symbian + Symbian OS
-  # http://en.wikipedia.org/wiki/History_of_Symbian
-  ##########
-  - regex: '(Symbian[Oo][Ss])[/ ](\d+)\.(\d+)'
-    os_replacement: 'Symbian OS'
-  - regex: '(Symbian/3).+NokiaBrowser/7\.3'
-    os_replacement: 'Symbian^3 Anna'
-  - regex: '(Symbian/3).+NokiaBrowser/7\.4'
-    os_replacement: 'Symbian^3 Belle'
-  - regex: '(Symbian/3)'
-    os_replacement: 'Symbian^3'
-  - regex: '\b(Series 60|SymbOS|S60Version|S60V\d|S60\b)'
-    os_replacement: 'Symbian OS'
-  - regex: '(MeeGo)'
-  - regex: 'Symbian [Oo][Ss]'
-    os_replacement: 'Symbian OS'
-  - regex: 'Series40;'
-    os_replacement: 'Nokia Series 40'
-  - regex: 'Series30Plus;'
-    os_replacement: 'Nokia Series 30 Plus'
-
-  ##########
-  # BlackBerry devices
-  ##########
-  - regex: '(BB10);.+Version/(\d+)\.(\d+)\.(\d+)'
-    os_replacement: 'BlackBerry OS'
-  - regex: '(Black[Bb]erry)[0-9a-z]+/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'
-    os_replacement: 'BlackBerry OS'
-  - regex: '(Black[Bb]erry).+Version/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'
-    os_replacement: 'BlackBerry OS'
-  - regex: '(RIM Tablet OS) (\d+)\.(\d+)\.(\d+)'
-    os_replacement: 'BlackBerry Tablet OS'
-  - regex: '(Play[Bb]ook)'
-    os_replacement: 'BlackBerry Tablet OS'
-  - regex: '(Black[Bb]erry)'
-    os_replacement: 'BlackBerry OS'
-
-  ##########
-  # Firefox OS
-  ##########
-  - regex: '\((?:Mobile|Tablet);.+Gecko/18.0 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '1'
-    os_v2_replacement: '0'
-    os_v3_replacement: '1'
-
-  - regex: '\((?:Mobile|Tablet);.+Gecko/18.1 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '1'
-    os_v2_replacement: '1'
-
-  - regex: '\((?:Mobile|Tablet);.+Gecko/26.0 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '1'
-    os_v2_replacement: '2'
-
-  - regex: '\((?:Mobile|Tablet);.+Gecko/28.0 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '1'
-    os_v2_replacement: '3'
-
-  - regex: '\((?:Mobile|Tablet);.+Gecko/30.0 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '1'
-    os_v2_replacement: '4'
-
-  - regex: '\((?:Mobile|Tablet);.+Gecko/32.0 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '2'
-    os_v2_replacement: '0'
-
-  - regex: '\((?:Mobile|Tablet);.+Gecko/34.0 Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-    os_v1_replacement: '2'
-    os_v2_replacement: '1'
-
-  # Firefox OS Generic
-  - regex: '\((?:Mobile|Tablet);.+Firefox/\d+\.\d+'
-    os_replacement: 'Firefox OS'
-
-
-  ##########
-  # BREW
-  # yes, Brew is lower-cased for Brew MP
-  ##########
-  - regex: '(BREW)[ /](\d+)\.(\d+)\.(\d+)'
-  - regex: '(BREW);'
-  - regex: '(Brew MP|BMP)[ /](\d+)\.(\d+)\.(\d+)'
-    os_replacement: 'Brew MP'
-  - regex: 'BMP;'
-    os_replacement: 'Brew MP'
-
-  ##########
-  # Google TV
-  ##########
-  - regex: '(GoogleTV)(?: (\d+)\.(\d+)(?:\.(\d+))?|/[\da-z]+)'
-
-  - regex: '(WebTV)/(\d+).(\d+)'
-
-  ##########
-  # Chromecast
-  ##########
-  - regex: '(CrKey)(?:[/](\d+)\.(\d+)(?:\.(\d+))?)?'
-    os_replacement: 'Chromecast'
-
-  ##########
-  # Misc mobile
-  ##########
-  - regex: '(hpw|web)OS/(\d+)\.(\d+)(?:\.(\d+))?'
-    os_replacement: 'webOS'
-  - regex: '(VRE);'
-
-  ##########
-  # Generic patterns
-  # since the majority of os cases are very specific, these go last
-  ##########
-  - regex: '(Fedora|Red Hat|PCLinuxOS|Puppy|Ubuntu|Kindle|Bada|Lubuntu|BackTrack|Slackware|(?:Free|Open|Net|\b)BSD)[/ ](\d+)\.(\d+)(?:\.(\d+)(?:\.(\d+))?)?'
-
-  # Gentoo Linux + Kernel Version
-  - regex: '(Linux)[ /](\d+)\.(\d+)(?:\.(\d+))?.*gentoo'
-    os_replacement: 'Gentoo'
-
-  # Opera Mini Bada
-  - regex: '\((Bada);'
-
-  # just os
-  - regex: '(Windows|Android|WeTab|Maemo|Web0S)'
-  - regex: '(Ubuntu|Kubuntu|Arch Linux|CentOS|Slackware|Gentoo|openSUSE|SUSE|Red Hat|Fedora|PCLinuxOS|Mageia|(?:Free|Open|Net|\b)BSD)'
-  # Linux + Kernel Version
-  - regex: '(Linux)(?:[ /](\d+)\.(\d+)(?:\.(\d+))?)?'
-  - regex: 'SunOS'
-    os_replacement: 'Solaris'
-  # Wget/x.x.x (linux-gnu)
-  - regex: '\(linux-gnu\)'
-    os_replacement: 'Linux'
-  - regex: '\(x86_64-redhat-linux-gnu\)'
-    os_replacement: 'Red Hat'
-  - regex: '\((freebsd)(\d+)\.(\d+)\)'
-    os_replacement: 'FreeBSD'
-
-  # Roku Digital-Video-Players https://www.roku.com/
-  - regex: '^(Roku)/DVP-(\d+)\.(\d+)'
-
-device_parsers:
-
-  #########
-  # Mobile Spiders
-  # Catch the mobile crawler before checking for iPhones / Androids.
-  #########
-  - regex: '(?:(?:iPhone|Windows CE|Windows Phone|Android).*(?:(?:Bot|Yeti)-Mobile|YRSpider|BingPreview|bots?/\d|(?:bot|spider)\.html)|AdsBot-Google-Mobile.*iPhone)'
-    regex_flag: 'i'
-    device_replacement: 'Spider'
-    brand_replacement: 'Spider'
-    model_replacement: 'Smartphone'
-  - regex: '(?:DoCoMo|\bMOT\b|\bLG\b|Nokia|Samsung|SonyEricsson).*(?:(?:Bot|Yeti)-Mobile|bots?/\d|(?:bot|crawler)\.html|(?:jump|google|Wukong)bot|ichiro/mobile|/spider|YahooSeeker)'
-    regex_flag: 'i'
-    device_replacement: 'Spider'
-    brand_replacement: 'Spider'
-    model_replacement: 'Feature Phone'
-
-  #########
-  # WebBrowser for SmartWatch
-  # @ref: https://play.google.com/store/apps/details?id=se.vaggan.webbrowser&hl=en
-  #########
-  - regex: '\bSmartWatch *\( *([^;]+) *; *([^;]+) *;'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  ######################################################################
-  # Android parsers
-  #
-  # @ref: https://support.google.com/googleplay/answer/1727131?hl=en
-  ######################################################################
-
-  # Android Application
-  - regex: 'Android Application[^\-]+ - (Sony) ?(Ericsson)? (.+) \w+ - '
-    device_replacement: '$1 $2'
-    brand_replacement: '$1$2'
-    model_replacement: '$3'
-  - regex: 'Android Application[^\-]+ - (?:HTC|HUAWEI|LGE|LENOVO|MEDION|TCT) (HTC|HUAWEI|LG|LENOVO|MEDION|ALCATEL)[ _\-](.+) \w+ - '
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  - regex: 'Android Application[^\-]+ - ([^ ]+) (.+) \w+ - '
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # 3Q
-  # @ref: http://www.3q-int.com/
-  #########
-  - regex: '; *([BLRQ]C\d{4}[A-Z]+) +Build/'
-    device_replacement: '3Q $1'
-    brand_replacement: '3Q'
-    model_replacement: '$1'
-  - regex: '; *(?:3Q_)([^;/]+) +Build'
-    device_replacement: '3Q $1'
-    brand_replacement: '3Q'
-    model_replacement: '$1'
-
-  #########
-  # Acer
-  # @ref: http://us.acer.com/ac/en/US/content/group/tablets
-  #########
-  - regex: 'Android [34].*; *(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700(?: Lite| 3G)?|A701|B1-A71|A1-\d{3}|B1-\d{3}|V360|V370|W500|W500P|W501|W501P|W510|W511|W700|Slider SL101|DA22[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Acer'
-    model_replacement: '$1'
-  - regex: '; *Acer Iconia Tab ([^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Acer'
-    model_replacement: '$1'
-  - regex: '; *(Z1[1235]0|E320[^/]*|S500|S510|Liquid[^;/]*|Iconia A\d+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Acer'
-    model_replacement: '$1'
-  - regex: '; *(Acer |ACER )([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Acer'
-    model_replacement: '$2'
-
-  #########
-  # Advent
-  # @ref: https://en.wikipedia.org/wiki/Advent_Vega
-  # @note: VegaBean and VegaComb (names derived from jellybean, honeycomb) are
-  #   custom ROM builds for Vega
-  #########
-  - regex: '; *(Advent )?(Vega(?:Bean|Comb)?).* Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Advent'
-    model_replacement: '$2'
-
-  #########
-  # Ainol
-  # @ref: http://www.ainol.com/plugin.php?identifier=ainol&module=product
-  #########
-  - regex: '; *(Ainol )?((?:NOVO|[Nn]ovo)[^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Ainol'
-    model_replacement: '$2'
-
-  #########
-  # Airis
-  # @ref: http://airis.es/Tienda/Default.aspx?idG=001
-  #########
-  - regex: '; *AIRIS[ _\-]?([^/;\)]+) *(?:;|\)|Build)'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Airis'
-    model_replacement: '$1'
-  - regex: '; *(OnePAD[^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Airis'
-    model_replacement: '$1'
-
-  #########
-  # Airpad
-  # @ref: ??
-  #########
-  - regex: '; *Airpad[ \-]([^;/]+) Build'
-    device_replacement: 'Airpad $1'
-    brand_replacement: 'Airpad'
-    model_replacement: '$1'
-
-  #########
-  # Alcatel - TCT
-  # @ref: http://www.alcatelonetouch.com/global-en/products/smartphones.html
-  #########
-  - regex: '; *(one ?touch) (EVO7|T10|T20) Build'
-    device_replacement: 'Alcatel One Touch $2'
-    brand_replacement: 'Alcatel'
-    model_replacement: 'One Touch $2'
-  - regex: '; *(?:alcatel[ _])?(?:(?:one[ _]?touch[ _])|ot[ \-])([^;/]+);? Build'
-    regex_flag: 'i'
-    device_replacement: 'Alcatel One Touch $1'
-    brand_replacement: 'Alcatel'
-    model_replacement: 'One Touch $1'
-  - regex: '; *(TCL)[ _]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  # operator specific models
-  - regex: '; *(Vodafone Smart II|Optimus_Madrid) Build'
-    device_replacement: 'Alcatel $1'
-    brand_replacement: 'Alcatel'
-    model_replacement: '$1'
-  - regex: '; *BASE_Lutea_3 Build'
-    device_replacement: 'Alcatel One Touch 998'
-    brand_replacement: 'Alcatel'
-    model_replacement: 'One Touch 998'
-  - regex: '; *BASE_Varia Build'
-    device_replacement: 'Alcatel One Touch 918D'
-    brand_replacement: 'Alcatel'
-    model_replacement: 'One Touch 918D'
-
-  #########
-  # Allfine
-  # @ref: http://www.myallfine.com/Products.asp
-  #########
-  - regex: '; *((?:FINE|Fine)\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Allfine'
-    model_replacement: '$1'
-
-  #########
-  # Allview
-  # @ref: http://www.allview.ro/produse/droseries/lista-tablete-pc/
-  #########
-  - regex: '; *(ALLVIEW[ _]?|Allview[ _]?)((?:Speed|SPEED).*) Build/'
-    device_replacement: '$1$2'
-    brand_replacement: 'Allview'
-    model_replacement: '$2'
-  - regex: '; *(ALLVIEW[ _]?|Allview[ _]?)?(AX1_Shine|AX2_Frenzy) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Allview'
-    model_replacement: '$2'
-  - regex: '; *(ALLVIEW[ _]?|Allview[ _]?)([^;/]*) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Allview'
-    model_replacement: '$2'
-
-  #########
-  # Allwinner
-  # @ref: http://www.allwinner.com/
-  # @models: A31 (13.3"),A20,A10,
-  #########
-  - regex: '; *(A13-MID) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Allwinner'
-    model_replacement: '$1'
-  - regex: '; *(Allwinner)[ _\-]?([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Allwinner'
-    model_replacement: '$1'
-
-  #########
-  # Amaway
-  # @ref: http://www.amaway.cn/
-  #########
-  - regex: '; *(A651|A701B?|A702|A703|A705|A706|A707|A711|A712|A713|A717|A722|A785|A801|A802|A803|A901|A902|A1002|A1003|A1006|A1007|A9701|A9703|Q710|Q80) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Amaway'
-    model_replacement: '$1'
-
-  #########
-  # Amoi
-  # @ref: http://www.amoi.com/en/prd/prd_index.jspx
-  #########
-  - regex: '; *(?:AMOI|Amoi)[ _]([^;/]+) Build'
-    device_replacement: 'Amoi $1'
-    brand_replacement: 'Amoi'
-    model_replacement: '$1'
-  - regex: '^(?:AMOI|Amoi)[ _]([^;/]+) Linux'
-    device_replacement: 'Amoi $1'
-    brand_replacement: 'Amoi'
-    model_replacement: '$1'
-
-  #########
-  # Aoc
-  # @ref: http://latin.aoc.com/media_tablet
-  #########
-  - regex: '; *(MW(?:0[789]|10)[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Aoc'
-    model_replacement: '$1'
-
-  #########
-  # Aoson
-  # @ref: http://www.luckystar.com.cn/en/mid.aspx?page=1
-  # @ref: http://www.luckystar.com.cn/en/mobiletel.aspx?page=1
-  # @note: brand owned by luckystar
-  #########
-  - regex: '; *(G7|M1013|M1015G|M11[CG]?|M-?12[B]?|M15|M19[G]?|M30[ACQ]?|M31[GQ]|M32|M33[GQ]|M36|M37|M38|M701T|M710|M712B|M713|M715G|M716G|M71(?:G|GS|T)?|M72[T]?|M73[T]?|M75[GT]?|M77G|M79T|M7L|M7LN|M81|M810|M81T|M82|M92|M92KS|M92S|M717G|M721|M722G|M723|M725G|M739|M785|M791|M92SK|M93D) Build'
-    device_replacement: 'Aoson $1'
-    brand_replacement: 'Aoson'
-    model_replacement: '$1'
-  - regex: '; *Aoson ([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: 'Aoson $1'
-    brand_replacement: 'Aoson'
-    model_replacement: '$1'
-
-  #########
-  # Apanda
-  # @ref: http://www.apanda.com.cn/
-  #########
-  - regex: '; *[Aa]panda[ _\-]([^;/]+) Build'
-    device_replacement: 'Apanda $1'
-    brand_replacement: 'Apanda'
-    model_replacement: '$1'
-
-  #########
-  # Archos
-  # @ref: http://www.archos.com/de/products/tablets.html
-  # @ref: http://www.archos.com/de/products/smartphones/index.html
-  #########
-  - regex: '; *(?:ARCHOS|Archos) ?(GAMEPAD.*?)(?: Build|[;/\(\)\-])'
-    device_replacement: 'Archos $1'
-    brand_replacement: 'Archos'
-    model_replacement: '$1'
-  - regex: 'ARCHOS; GOGI; ([^;]+);'
-    device_replacement: 'Archos $1'
-    brand_replacement: 'Archos'
-    model_replacement: '$1'
-  - regex: '(?:ARCHOS|Archos)[ _]?(.*?)(?: Build|[;/\(\)\-]|$)'
-    device_replacement: 'Archos $1'
-    brand_replacement: 'Archos'
-    model_replacement: '$1'
-  - regex: '; *(AN(?:7|8|9|10|13)[A-Z0-9]{1,4}) Build'
-    device_replacement: 'Archos $1'
-    brand_replacement: 'Archos'
-    model_replacement: '$1'
-  - regex: '; *(A28|A32|A43|A70(?:BHT|CHT|HB|S|X)|A101(?:B|C|IT)|A7EB|A7EB-WK|101G9|80G9) Build'
-    device_replacement: 'Archos $1'
-    brand_replacement: 'Archos'
-    model_replacement: '$1'
-
-  #########
-  # A-rival
-  # @ref: http://www.a-rival.de/de/
-  #########
-  - regex: '; *(PAD-FMD[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Arival'
-    model_replacement: '$1'
-  - regex: '; *(BioniQ) ?([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Arival'
-    model_replacement: '$1 $2'
-
-  #########
-  # Arnova
-  # @ref: http://arnovatech.com/
-  #########
-  - regex: '; *(AN\d[^;/]+|ARCHM\d+) Build'
-    device_replacement: 'Arnova $1'
-    brand_replacement: 'Arnova'
-    model_replacement: '$1'
-  - regex: '; *(?:ARNOVA|Arnova) ?([^;/]+) Build'
-    device_replacement: 'Arnova $1'
-    brand_replacement: 'Arnova'
-    model_replacement: '$1'
-
-  #########
-  # Assistant
-  # @ref: http://www.assistant.ua
-  #########
-  - regex: '; *(?:ASSISTANT )?(AP)-?([1789]\d{2}[A-Z]{0,2}|80104) Build'
-    device_replacement: 'Assistant $1-$2'
-    brand_replacement: 'Assistant'
-    model_replacement: '$1-$2'
-
-  #########
-  # Asus
-  # @ref: http://www.asus.com/uk/Tablets_Mobile/
-  #########
-  - regex: '; *(ME17\d[^;/]*|ME3\d{2}[^;/]+|K00[A-Z]|Nexus 10|Nexus 7(?: 2013)?|PadFone[^;/]*|Transformer[^;/]*|TF\d{3}[^;/]*|eeepc) Build'
-    device_replacement: 'Asus $1'
-    brand_replacement: 'Asus'
-    model_replacement: '$1'
-  - regex: '; *ASUS[ _]*([^;/]+) Build'
-    device_replacement: 'Asus $1'
-    brand_replacement: 'Asus'
-    model_replacement: '$1'
-
-  #########
-  # Garmin-Asus
-  #########
-  - regex: '; *Garmin-Asus ([^;/]+) Build'
-    device_replacement: 'Garmin-Asus $1'
-    brand_replacement: 'Garmin-Asus'
-    model_replacement: '$1'
-  - regex: '; *(Garminfone) Build'
-    device_replacement: 'Garmin $1'
-    brand_replacement: 'Garmin-Asus'
-    model_replacement: '$1'
-
-  #########
-  # Attab
-  # @ref: http://www.theattab.com/
-  #########
-  - regex: '; (@TAB-[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Attab'
-    model_replacement: '$1'
-
-  #########
-  # Audiosonic
-  # @ref: ??
-  # @note: Take care with Docomo T-01 Toshiba
-  #########
-  - regex: '; *(T-(?:07|[^0]\d)[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Audiosonic'
-    model_replacement: '$1'
-
-  #########
-  # Axioo
-  # @ref: http://www.axiooworld.com/ww/index.php
-  #########
-  - regex: '; *(?:Axioo[ _\-]([^;/]+)|(picopad)[ _\-]([^;/]+)) Build'
-    regex_flag: 'i'
-    device_replacement: 'Axioo $1$2 $3'
-    brand_replacement: 'Axioo'
-    model_replacement: '$1$2 $3'
-
-  #########
-  # Azend
-  # @ref: http://azendcorp.com/index.php/products/portable-electronics
-  #########
-  - regex: '; *(V(?:100|700|800)[^;/]*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Azend'
-    model_replacement: '$1'
-
-  #########
-  # Bak
-  # @ref: http://www.bakinternational.com/produtos.php?cat=80
-  #########
-  - regex: '; *(IBAK\-[^;/]*) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Bak'
-    model_replacement: '$1'
-
-  #########
-  # Bedove
-  # @ref: http://www.bedove.com/product.html
-  # @models: HY6501|HY5001|X12|X21|I5
-  #########
-  - regex: '; *(HY5001|HY6501|X12|X21|I5) Build'
-    device_replacement: 'Bedove $1'
-    brand_replacement: 'Bedove'
-    model_replacement: '$1'
-
-  #########
-  # Benss
-  # @ref: http://www.benss.net/
-  #########
-  - regex: '; *(JC-[^;/]*) Build'
-    device_replacement: 'Benss $1'
-    brand_replacement: 'Benss'
-    model_replacement: '$1'
-
-  #########
-  # Blackberry
-  # @ref: http://uk.blackberry.com/
-  # @note: Android Apps seams to be used here
-  #########
-  - regex: '; *(BB) ([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Blackberry'
-    model_replacement: '$2'
-
-  #########
-  # Blackbird
-  # @ref: http://iblackbird.co.kr
-  #########
-  - regex: '; *(BlackBird)[ _](I8.*) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  - regex: '; *(BlackBird)[ _](.*) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Blaupunkt
-  # @ref: http://www.blaupunkt.com
-  #########
-  # Endeavour
-  - regex: '; *([0-9]+BP[EM][^;/]*|Endeavour[^;/]+) Build'
-    device_replacement: 'Blaupunkt $1'
-    brand_replacement: 'Blaupunkt'
-    model_replacement: '$1'
-
-  #########
-  # Blu
-  # @ref: http://bluproducts.com
-  #########
-  - regex: '; *((?:BLU|Blu)[ _\-])([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Blu'
-    model_replacement: '$2'
-  # BMOBILE = operator branded device
-  - regex: '; *(?:BMOBILE )?(Blu|BLU|DASH [^;/]+|VIVO 4\.3|TANK 4\.5) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Blu'
-    model_replacement: '$1'
-
-  #########
-  # Blusens
-  # @ref: http://www.blusens.com/es/?sg=1&sv=al&roc=1
-  #########
-  # tablet
-  - regex: '; *(TOUCH\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Blusens'
-    model_replacement: '$1'
-
-  #########
-  # Bmobile
-  # @ref: http://bmobile.eu.com/?categoria=smartphones-2
-  # @note: Might collide with Maxx as AX is used also there.
-  #########
-  # smartphone
-  - regex: '; *(AX5\d+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Bmobile'
-    model_replacement: '$1'
-
-  #########
-  # bq
-  # @ref: http://bqreaders.com
-  #########
-  - regex: '; *([Bb]q) ([^;/]+);? Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'bq'
-    model_replacement: '$2'
-  - regex: '; *(Maxwell [^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'bq'
-    model_replacement: '$1'
-
-  #########
-  # Braun Phototechnik
-  # @ref: http://www.braun-phototechnik.de/en/products/list/~pcat.250/Tablet-PC.html
-  #########
-  - regex: '; *((?:B-Tab|B-TAB) ?\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Braun'
-    model_replacement: '$1'
-
-  #########
-  # Broncho
-  # @ref: http://www.broncho.cn/
-  #########
-  - regex: '; *(Broncho) ([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Captiva
-  # @ref: http://www.captiva-power.de
-  #########
-  - regex: '; *CAPTIVA ([^;/]+) Build'
-    device_replacement: 'Captiva $1'
-    brand_replacement: 'Captiva'
-    model_replacement: '$1'
-
-  #########
-  # Casio
-  # @ref: http://www.casiogzone.com/
-  #########
-  - regex: '; *(C771|CAL21|IS11CA) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Casio'
-    model_replacement: '$1'
-
-  #########
-  # Cat
-  # @ref: http://www.cat-sound.com
-  #########
-  - regex: '; *(?:Cat|CAT) ([^;/]+) Build'
-    device_replacement: 'Cat $1'
-    brand_replacement: 'Cat'
-    model_replacement: '$1'
-  - regex: '; *(?:Cat)(Nova.*) Build'
-    device_replacement: 'Cat $1'
-    brand_replacement: 'Cat'
-    model_replacement: '$1'
-  - regex: '; *(INM8002KP|ADM8000KP_[AB]) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Cat'
-    model_replacement: 'Tablet PHOENIX 8.1J0'
-
-  #########
-  # Celkon
-  # @ref: http://www.celkonmobiles.com/?_a=products
-  # @models: A10, A19Q, A101, A105, A107, A107\+, A112, A118, A119, A119Q, A15, A19, A20, A200, A220, A225, A22 Race, A27, A58, A59, A60, A62, A63, A64, A66, A67, A69, A75, A77, A79, A8\+, A83, A85, A86, A87, A89 Ultima, A9\+, A90, A900, A95, A97i, A98, AR 40, AR 45, AR 50, ML5
-  #########
-  - regex: '; *(?:[Cc]elkon[ _\*]|CELKON[ _\*])([^;/\)]+) ?(?:Build|;|\))'
-    device_replacement: '$1'
-    brand_replacement: 'Celkon'
-    model_replacement: '$1'
-  - regex: 'Build/(?:[Cc]elkon)+_?([^;/_\)]+)'
-    device_replacement: '$1'
-    brand_replacement: 'Celkon'
-    model_replacement: '$1'
-  - regex: '; *(CT)-?(\d+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Celkon'
-    model_replacement: '$1$2'
-  # smartphones
-  - regex: '; *(A19|A19Q|A105|A107[^;/\)]*) ?(?:Build|;|\))'
-    device_replacement: '$1'
-    brand_replacement: 'Celkon'
-    model_replacement: '$1'
-
-  #########
-  # ChangJia
-  # @ref: http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
-  # @brief: China manufacturer makes tablets for different small brands
-  #         (eg. http://www.zeepad.net/index.html)
-  #########
-  - regex: '; *(TPC[0-9]{4,5}) Build'
-    device_replacement: '$1'
-    brand_replacement: 'ChangJia'
-    model_replacement: '$1'
-
-  #########
-  # Cloudfone
-  # @ref: http://www.cloudfonemobile.com/
-  #########
-  - regex: '; *(Cloudfone)[ _](Excite)([^ ][^;/]+) Build'
-    device_replacement: '$1 $2 $3'
-    brand_replacement: 'Cloudfone'
-    model_replacement: '$1 $2 $3'
-  - regex: '; *(Excite|ICE)[ _](\d+[^;/]+) Build'
-    device_replacement: 'Cloudfone $1 $2'
-    brand_replacement: 'Cloudfone'
-    model_replacement: 'Cloudfone $1 $2'
-  - regex: '; *(Cloudfone|CloudPad)[ _]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Cloudfone'
-    model_replacement: '$1 $2'
-
-  #########
-  # Cmx
-  # @ref: http://cmx.at/de/
-  #########
-  - regex: '; *((?:Aquila|Clanga|Rapax)[^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Cmx'
-    model_replacement: '$1'
-
-  #########
-  # CobyKyros
-  # @ref: http://cobykyros.com
-  # @note: Be careful with MID\d{3} from MpMan or Manta
-  #########
-  - regex: '; *(?:CFW-|Kyros )?(MID[0-9]{4}(?:[ABC]|SR|TV)?)(\(3G\)-4G| GB 8K| 3G| 8K| GB)? *(?:Build|[;\)])'
-    device_replacement: 'CobyKyros $1$2'
-    brand_replacement: 'CobyKyros'
-    model_replacement: '$1$2'
-
-  #########
-  # Coolpad
-  # @ref: ??
-  #########
-  - regex: '; *([^;/]*)Coolpad[ _]([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Coolpad'
-    model_replacement: '$1$2'
-
-  #########
-  # Cube
-  # @ref: http://www.cube-tablet.com/buy-products.html
-  #########
-  - regex: '; *(CUBE[ _])?([KU][0-9]+ ?GT.*|A5300) Build'
-    regex_flag: 'i'
-    device_replacement: '$1$2'
-    brand_replacement: 'Cube'
-    model_replacement: '$2'
-
-  #########
-  # Cubot
-  # @ref: http://www.cubotmall.com/
-  #########
-  - regex: '; *CUBOT ([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Cubot'
-    model_replacement: '$1'
-  - regex: '; *(BOBBY) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Cubot'
-    model_replacement: '$1'
-
-  #########
-  # Danew
-  # @ref: http://www.danew.com/produits-tablette.php
-  #########
-  - regex: '; *(Dslide [^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Danew'
-    model_replacement: '$1'
-
-  #########
-  # Dell
-  # @ref: http://www.dell.com
-  # @ref: http://www.softbank.jp/mobile/support/product/101dl/
-  # @ref: http://www.softbank.jp/mobile/support/product/001dl/
-  # @ref: http://developer.emnet.ne.jp/android.html
-  # @ref: http://www.dell.com/in/p/mobile-xcd28/pd
-  # @ref: http://www.dell.com/in/p/mobile-xcd35/pd
-  #########
-  - regex: '; *(XCD)[ _]?(28|35) Build'
-    device_replacement: 'Dell $1$2'
-    brand_replacement: 'Dell'
-    model_replacement: '$1$2'
-  - regex: '; *(001DL) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: 'Streak'
-  - regex: '; *(?:Dell|DELL) (Streak) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: 'Streak'
-  - regex: '; *(101DL|GS01|Streak Pro[^;/]*) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: 'Streak Pro'
-  - regex: '; *([Ss]treak ?7) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: 'Streak 7'
-  - regex: '; *(Mini-3iX) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: '$1'
-  - regex: '; *(?:Dell|DELL)[ _](Aero|Venue|Thunder|Mini.*|Streak[ _]Pro) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: '$1'
-  - regex: '; *Dell[ _]([^;/]+) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: '$1'
-  - regex: '; *Dell ([^;/]+) Build'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: '$1'
-
-  #########
-  # Denver
-  # @ref: http://www.denver-electronics.com/tablets1/
-  #########
-  - regex: '; *(TA[CD]-\d+[^;/]*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Denver'
-    model_replacement: '$1'
-
-  #########
-  # Dex
-  # @ref: http://dex.ua/
-  #########
-  - regex: '; *(iP[789]\d{2}(?:-3G)?|IP10\d{2}(?:-8GB)?) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Dex'
-    model_replacement: '$1'
-
-  #########
-  # DNS AirTab
-  # @ref: http://www.dns-shop.ru/
-  #########
-  - regex: '; *(AirTab)[ _\-]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'DNS'
-    model_replacement: '$1 $2'
-
-  #########
-  # Docomo (Operator Branded Device)
-  # @ref: http://www.ipentec.com/document/document.aspx?page=android-useragent
-  #########
-  - regex: '; *(F\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Fujitsu'
-    model_replacement: '$1'
-  - regex: '; *(HT-03A) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'Magic'
-  - regex: '; *(HT\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  - regex: '; *(L\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-  - regex: '; *(N\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Nec'
-    model_replacement: '$1'
-  - regex: '; *(P\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Panasonic'
-    model_replacement: '$1'
-  - regex: '; *(SC\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  - regex: '; *(SH\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sharp'
-    model_replacement: '$1'
-  - regex: '; *(SO\-\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: '$1'
-  - regex: '; *(T\-0[12][^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Toshiba'
-    model_replacement: '$1'
-
-  #########
-  # DOOV
-  # @ref: http://www.doov.com.cn/
-  #########
-  - regex: '; *(DOOV)[ _]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'DOOV'
-    model_replacement: '$2'
-
-  #########
-  # Enot
-  # @ref: http://www.enot.ua/
-  #########
-  - regex: '; *(Enot|ENOT)[ -]?([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Enot'
-    model_replacement: '$2'
-
-  #########
-  # Evercoss
-  # @ref: http://evercoss.com/android/
-  #########
-  - regex: '; *[^;/]+ Build/(?:CROSS|Cross)+[ _\-]([^\)]+)'
-    device_replacement: 'CROSS $1'
-    brand_replacement: 'Evercoss'
-    model_replacement: 'Cross $1'
-  - regex: '; *(CROSS|Cross)[ _\-]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Evercoss'
-    model_replacement: 'Cross $2'
-
-  #########
-  # Explay
-  # @ref: http://explay.ru/
-  #########
-  - regex: '; *Explay[_ ](.+?)(?:[\)]| Build)'
-    device_replacement: '$1'
-    brand_replacement: 'Explay'
-    model_replacement: '$1'
-
-  #########
-  # Fly
-  # @ref: http://www.fly-phone.com/
-  #########
-  - regex: '; *(IQ.*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Fly'
-    model_replacement: '$1'
-  - regex: '; *(Fly|FLY)[ _](IQ[^;]+|F[34]\d+[^;]*);? Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Fly'
-    model_replacement: '$2'
-
-  #########
-  # Fujitsu
-  # @ref: http://www.fujitsu.com/global/
-  #########
-  - regex: '; *(M532|Q572|FJL21) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Fujitsu'
-    model_replacement: '$1'
-
-  #########
-  # Galapad
-  # @ref: http://www.galapad.net/product.html
-  #########
-  - regex: '; *(G1) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Galapad'
-    model_replacement: '$1'
-
-  #########
-  # Geeksphone
-  # @ref: http://www.geeksphone.com/
-  #########
-  - regex: '; *(Geeksphone) ([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Gfive
-  # @ref: http://www.gfivemobile.com/en
-  #########
-  #- regex: '; *(G\'?FIVE) ([^;/]+) Build' # there is a problem with python yaml parser here
-  - regex: '; *(G[^F]?FIVE) ([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Gfive'
-    model_replacement: '$2'
-
-  #########
-  # Gionee
-  # @ref: http://www.gionee.com/
-  #########
-  - regex: '; *(Gionee)[ _\-]([^;/]+)(?:/[^;/]+)? Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Gionee'
-    model_replacement: '$2'
-  - regex: '; *(GN\d+[A-Z]?|INFINITY_PASSION|Ctrl_V1) Build'
-    device_replacement: 'Gionee $1'
-    brand_replacement: 'Gionee'
-    model_replacement: '$1'
-  - regex: '; *(E3) Build/JOP40D'
-    device_replacement: 'Gionee $1'
-    brand_replacement: 'Gionee'
-    model_replacement: '$1'
-  - regex: '\sGIONEE[-\s_](\w*)'
-    regex_flag: 'i'
-    device_replacement: 'Gionee $1'
-    brand_replacement: 'Gionee'
-    model_replacement: '$1'
-
-  #########
-  # GoClever
-  # @ref: http://www.goclever.com
-  #########
-  - regex: '; *((?:FONE|QUANTUM|INSIGNIA) \d+[^;/]*|PLAYTAB) Build'
-    device_replacement: 'GoClever $1'
-    brand_replacement: 'GoClever'
-    model_replacement: '$1'
-  - regex: '; *GOCLEVER ([^;/]+) Build'
-    device_replacement: 'GoClever $1'
-    brand_replacement: 'GoClever'
-    model_replacement: '$1'
-
-  #########
-  # Google
-  # @ref: http://www.google.de/glass/start/
-  #########
-  - regex: '; *(Glass \d+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Google'
-    model_replacement: '$1'
-  - regex: '; *(Pixel.*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Google'
-    model_replacement: '$1'
-
-  #########
-  # Gigabyte
-  # @ref: http://gsmart.gigabytecm.com/en/
-  #########
-  - regex: '; *(GSmart)[ -]([^/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Gigabyte'
-    model_replacement: '$1 $2'
-
-  #########
-  # Freescale development boards
-  # @ref: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=IMX53QSB
-  #########
-  - regex: '; *(imx5[13]_[^/]+) Build'
-    device_replacement: 'Freescale $1'
-    brand_replacement: 'Freescale'
-    model_replacement: '$1'
-
-  #########
-  # Haier
-  # @ref: http://www.haier.com/
-  # @ref: http://www.haier.com/de/produkte/tablet/
-  #########
-  - regex: '; *Haier[ _\-]([^/]+) Build'
-    device_replacement: 'Haier $1'
-    brand_replacement: 'Haier'
-    model_replacement: '$1'
-  - regex: '; *(PAD1016) Build'
-    device_replacement: 'Haipad $1'
-    brand_replacement: 'Haipad'
-    model_replacement: '$1'
-
-  #########
-  # Haipad
-  # @ref: http://www.haipad.net/
-  # @models: V7P|M7SM7S|M9XM9X|M7XM7X|M9|M8|M7-M|M1002|M7|M701
-  #########
-  - regex: '; *(M701|M7|M8|M9) Build'
-    device_replacement: 'Haipad $1'
-    brand_replacement: 'Haipad'
-    model_replacement: '$1'
-
-  #########
-  # Hannspree
-  # @ref: http://www.hannspree.eu/
-  # @models: SN10T1|SN10T2|SN70T31B|SN70T32W
-  #########
-  - regex: '; *(SN\d+T[^;\)/]*)(?: Build|[;\)])'
-    device_replacement: 'Hannspree $1'
-    brand_replacement: 'Hannspree'
-    model_replacement: '$1'
-
-  #########
-  # HCLme
-  # @ref: http://www.hclmetablet.com/india/
-  #########
-  - regex: 'Build/HCL ME Tablet ([^;\)]+)[\);]'
-    device_replacement: 'HCLme $1'
-    brand_replacement: 'HCLme'
-    model_replacement: '$1'
-  - regex: '; *([^;\/]+) Build/HCL'
-    device_replacement: 'HCLme $1'
-    brand_replacement: 'HCLme'
-    model_replacement: '$1'
-
-  #########
-  # Hena
-  # @ref: http://www.henadigital.com/en/product/index.asp?id=6
-  #########
-  - regex: '; *(MID-?\d{4}C[EM]) Build'
-    device_replacement: 'Hena $1'
-    brand_replacement: 'Hena'
-    model_replacement: '$1'
-
-  #########
-  # Hisense
-  # @ref: http://www.hisense.com/
-  #########
-  - regex: '; *(EG\d{2,}|HS-[^;/]+|MIRA[^;/]+) Build'
-    device_replacement: 'Hisense $1'
-    brand_replacement: 'Hisense'
-    model_replacement: '$1'
-  - regex: '; *(andromax[^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: 'Hisense $1'
-    brand_replacement: 'Hisense'
-    model_replacement: '$1'
-
-  #########
-  # hitech
-  # @ref: http://www.hitech-mobiles.com/
-  #########
-  - regex: '; *(?:AMAZE[ _](S\d+)|(S\d+)[ _]AMAZE) Build'
-    device_replacement: 'AMAZE $1$2'
-    brand_replacement: 'hitech'
-    model_replacement: 'AMAZE $1$2'
-
-  #########
-  # HP
-  # @ref: http://www.hp.com/
-  #########
-  - regex: '; *(PlayBook) Build'
-    device_replacement: 'HP $1'
-    brand_replacement: 'HP'
-    model_replacement: '$1'
-  - regex: '; *HP ([^/]+) Build'
-    device_replacement: 'HP $1'
-    brand_replacement: 'HP'
-    model_replacement: '$1'
-  - regex: '; *([^/]+_tenderloin) Build'
-    device_replacement: 'HP TouchPad'
-    brand_replacement: 'HP'
-    model_replacement: 'TouchPad'
-
-  #########
-  # Huawei
-  # @ref: http://www.huaweidevice.com
-  # @note: Needs to be before HTC due to Desire HD Build on U8815
-  #########
-  - regex: '; *(HUAWEI |Huawei-)?([UY][^;/]+) Build/(?:Huawei|HUAWEI)([UY][^\);]+)\)'
-    device_replacement: '$1$2'
-    brand_replacement: 'Huawei'
-    model_replacement: '$2'
-  - regex: '; *([^;/]+) Build[/ ]Huawei(MT1-U06|[A-Z]+\d+[^\);]+)[^\);]*\)'
-    device_replacement: '$1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$2'
-  - regex: '; *(S7|M860) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-  - regex: '; *((?:HUAWEI|Huawei)[ \-]?)(MediaPad) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Huawei'
-    model_replacement: '$2'
-  - regex: '; *((?:HUAWEI[ _]?|Huawei[ _])?Ascend[ _])([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Huawei'
-    model_replacement: '$2'
-  - regex: '; *((?:HUAWEI|Huawei)[ _\-]?)((?:G700-|MT-)[^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Huawei'
-    model_replacement: '$2'
-  - regex: '; *((?:HUAWEI|Huawei)[ _\-]?)([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Huawei'
-    model_replacement: '$2'
-  - regex: '; *(MediaPad[^;]+|SpringBoard) Build/Huawei'
-    device_replacement: '$1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-  - regex: '; *([^;]+) Build/(?:Huawei|HUAWEI)'
-    device_replacement: '$1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-  - regex: '; *([Uu])([89]\d{3}) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Huawei'
-    model_replacement: 'U$2'
-  - regex: '; *(?:Ideos |IDEOS )(S7) Build'
-    device_replacement: 'Huawei Ideos$1'
-    brand_replacement: 'Huawei'
-    model_replacement: 'Ideos$1'
-  - regex: '; *(?:Ideos |IDEOS )([^;/]+\s*|\s*)Build'
-    device_replacement: 'Huawei Ideos$1'
-    brand_replacement: 'Huawei'
-    model_replacement: 'Ideos$1'
-  - regex: '; *(Orange Daytona|Pulse|Pulse Mini|Vodafone 858|C8500|C8600|C8650|C8660|Nexus 6P|ATH-.+?) Build[/ ]'
-    device_replacement: 'Huawei $1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-
-  #########
-  # HTC
-  # @ref: http://www.htc.com/www/products/
-  # @ref: http://en.wikipedia.org/wiki/List_of_HTC_phones
-  #########
-
-  - regex: '; *HTC[ _]([^;]+); Windows Phone'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-
-  # Android HTC with Version Number matcher
-  # ; HTC_0P3Z11/1.12.161.3 Build
-  # ;HTC_A3335 V2.38.841.1 Build
-  - regex: '; *(?:HTC[ _/])+([^ _/]+)(?:[/\\]1\.0 | V|/| +)\d+\.\d[\d\.]*(?: *Build|\))'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  - regex: '; *(?:HTC[ _/])+([^ _/]+)(?:[ _/]([^ _/]+))?(?:[/\\]1\.0 | V|/| +)\d+\.\d[\d\.]*(?: *Build|\))'
-    device_replacement: 'HTC $1 $2'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2'
-  - regex: '; *(?:HTC[ _/])+([^ _/]+)(?:[ _/]([^ _/]+)(?:[ _/]([^ _/]+))?)?(?:[/\\]1\.0 | V|/| +)\d+\.\d[\d\.]*(?: *Build|\))'
-    device_replacement: 'HTC $1 $2 $3'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2 $3'
-  - regex: '; *(?:HTC[ _/])+([^ _/]+)(?:[ _/]([^ _/]+)(?:[ _/]([^ _/]+)(?:[ _/]([^ _/]+))?)?)?(?:[/\\]1\.0 | V|/| +)\d+\.\d[\d\.]*(?: *Build|\))'
-    device_replacement: 'HTC $1 $2 $3 $4'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2 $3 $4'
-
-  # Android HTC without Version Number matcher
-  - regex: '; *(?:(?:HTC|htc)(?:_blocked)*[ _/])+([^ _/;]+)(?: *Build|[;\)]| - )'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  - regex: '; *(?:(?:HTC|htc)(?:_blocked)*[ _/])+([^ _/]+)(?:[ _/]([^ _/;\)]+))?(?: *Build|[;\)]| - )'
-    device_replacement: 'HTC $1 $2'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2'
-  - regex: '; *(?:(?:HTC|htc)(?:_blocked)*[ _/])+([^ _/]+)(?:[ _/]([^ _/]+)(?:[ _/]([^ _/;\)]+))?)?(?: *Build|[;\)]| - )'
-    device_replacement: 'HTC $1 $2 $3'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2 $3'
-  - regex: '; *(?:(?:HTC|htc)(?:_blocked)*[ _/])+([^ _/]+)(?:[ _/]([^ _/]+)(?:[ _/]([^ _/]+)(?:[ _/]([^ /;]+))?)?)?(?: *Build|[;\)]| - )'
-    device_replacement: 'HTC $1 $2 $3 $4'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2 $3 $4'
-
-  # HTC Streaming Player
-  - regex: 'HTC Streaming Player [^\/]*/[^\/]*/ htc_([^/]+) /'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  # general matcher for anything else
-  - regex: '(?:[;,] *|^)(?:htccn_chs-)?HTC[ _-]?([^;]+?)(?: *Build|clay|Android|-?Mozilla| Opera| Profile| UNTRUSTED|[;/\(\)]|$)'
-    regex_flag: 'i'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  # Android matchers without HTC
-  - regex: '; *(A6277|ADR6200|ADR6300|ADR6350|ADR6400[A-Z]*|ADR6425[A-Z]*|APX515CKT|ARIA|Desire[^_ ]*|Dream|EndeavorU|Eris|Evo|Flyer|HD2|Hero|HERO200|Hero CDMA|HTL21|Incredible|Inspire[A-Z0-9]*|Legend|Liberty|Nexus ?(?:One|HD2)|One|One S C2|One[ _]?(?:S|V|X\+?)\w*|PC36100|PG06100|PG86100|S31HT|Sensation|Wildfire)(?: Build|[/;\(\)])'
-    regex_flag: 'i'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  - regex: '; *(ADR6200|ADR6400L|ADR6425LVW|Amaze|DesireS?|EndeavorU|Eris|EVO|Evo\d[A-Z]+|HD2|IncredibleS?|Inspire[A-Z0-9]*|Inspire[A-Z0-9]*|Sensation[A-Z0-9]*|Wildfire)[ _-](.+?)(?:[/;\)]|Build|MIUI|1\.0)'
-    regex_flag: 'i'
-    device_replacement: 'HTC $1 $2'
-    brand_replacement: 'HTC'
-    model_replacement: '$1 $2'
-
-  #########
-  # Hyundai
-  # @ref: http://www.hyundaitechnologies.com
-  #########
-  - regex: '; *HYUNDAI (T\d[^/]*) Build'
-    device_replacement: 'Hyundai $1'
-    brand_replacement: 'Hyundai'
-    model_replacement: '$1'
-  - regex: '; *HYUNDAI ([^;/]+) Build'
-    device_replacement: 'Hyundai $1'
-    brand_replacement: 'Hyundai'
-    model_replacement: '$1'
-  # X900? http://www.amazon.com/Hyundai-X900-Retina-Android-Bluetooth/dp/B00AO07H3O
-  - regex: '; *(X700|Hold X|MB-6900) Build'
-    device_replacement: 'Hyundai $1'
-    brand_replacement: 'Hyundai'
-    model_replacement: '$1'
-
-  #########
-  # iBall
-  # @ref: http://www.iball.co.in/Category/Mobiles/22
-  #########
-  - regex: '; *(?:iBall[ _\-])?(Andi)[ _]?(\d[^;/]*) Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'iBall'
-    model_replacement: '$1 $2'
-  - regex: '; *(IBall)(?:[ _]([^;/]+))? Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'iBall'
-    model_replacement: '$2'
-
-  #########
-  # IconBIT
-  # @ref: http://www.iconbit.com/catalog/tablets/
-  #########
-  - regex: '; *(NT-\d+[^ ;/]*|Net[Tt]AB [^;/]+|Mercury [A-Z]+|iconBIT)(?: S/N:[^;/]+)? Build'
-    device_replacement: '$1'
-    brand_replacement: 'IconBIT'
-    model_replacement: '$1'
-
-  #########
-  # IMO
-  # @ref: http://www.ponselimo.com/
-  #########
-  - regex: '; *(IMO)[ _]([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'IMO'
-    model_replacement: '$2'
-
-  #########
-  # i-mobile
-  # @ref: http://www.i-mobilephone.com/
-  #########
-  - regex: '; *i-?mobile[ _]([^/]+) Build/'
-    regex_flag: 'i'
-    device_replacement: 'i-mobile $1'
-    brand_replacement: 'imobile'
-    model_replacement: '$1'
-  - regex: '; *(i-(?:style|note)[^/]*) Build/'
-    regex_flag: 'i'
-    device_replacement: 'i-mobile $1'
-    brand_replacement: 'imobile'
-    model_replacement: '$1'
-
-  #########
-  # Impression
-  # @ref: http://impression.ua/planshetnye-kompyutery
-  #########
-  - regex: '; *(ImPAD) ?(\d+(?:.)*) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Impression'
-    model_replacement: '$1 $2'
-
-  #########
-  # Infinix
-  # @ref: http://www.infinixmobility.com/index.html
-  #########
-  - regex: '; *(Infinix)[ _]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Infinix'
-    model_replacement: '$2'
-
-  #########
-  # Informer
-  # @ref: ??
-  #########
-  - regex: '; *(Informer)[ \-]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Informer'
-    model_replacement: '$2'
-
-  #########
-  # Intenso
-  # @ref: http://www.intenso.de
-  # @models: 7":TAB 714,TAB 724;8":TAB 814,TAB 824;10":TAB 1004
-  #########
-  - regex: '; *(TAB) ?([78][12]4) Build'
-    device_replacement: 'Intenso $1'
-    brand_replacement: 'Intenso'
-    model_replacement: '$1 $2'
-
-  #########
-  # Intex
-  # @ref: http://intexmobile.in/index.aspx
-  # @note: Zync also offers a "Cloud Z5" device
-  #########
-  # smartphones
-  - regex: '; *(?:Intex[ _])?(AQUA|Aqua)([ _\.\-])([^;/]+) *(?:Build|;)'
-    device_replacement: '$1$2$3'
-    brand_replacement: 'Intex'
-    model_replacement: '$1 $3'
-  # matches "INTEX CLOUD X1"
-  - regex: '; *(?:INTEX|Intex)(?:[_ ]([^\ _;/]+))(?:[_ ]([^\ _;/]+))? *(?:Build|;)'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Intex'
-    model_replacement: '$1 $2'
-  # tablets
-  - regex: '; *([iI]Buddy)[ _]?(Connect)(?:_|\?_| )?([^;/]*) *(?:Build|;)'
-    device_replacement: '$1 $2 $3'
-    brand_replacement: 'Intex'
-    model_replacement: 'iBuddy $2 $3'
-  - regex: '; *(I-Buddy)[ _]([^;/]+) *(?:Build|;)'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Intex'
-    model_replacement: 'iBuddy $2'
-
-  #########
-  # iOCEAN
-  # @ref: http://www.iocean.cc/
-  #########
-  - regex: '; *(iOCEAN) ([^/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'iOCEAN'
-    model_replacement: '$2'
-
-  #########
-  # i.onik
-  # @ref: http://www.i-onik.de/
-  #########
-  - regex: '; *(TP\d+(?:\.\d+)?\-\d[^;/]+) Build'
-    device_replacement: 'ionik $1'
-    brand_replacement: 'ionik'
-    model_replacement: '$1'
-
-  #########
-  # IRU.ru
-  # @ref: http://www.iru.ru/catalog/soho/planetable/
-  #########
-  - regex: '; *(M702pro) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Iru'
-    model_replacement: '$1'
-
-  #########
-  # Ivio
-  # @ref: http://www.ivio.com/mobile.php
-  # @models: DG80,DG20,DE38,DE88,MD70
-  #########
-  - regex: '; *(DE88Plus|MD70) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Ivio'
-    model_replacement: '$1'
-  - regex: '; *IVIO[_\-]([^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Ivio'
-    model_replacement: '$1'
-
-  #########
-  # Jaytech
-  # @ref: http://www.jay-tech.de/jaytech/servlet/frontend/
-  #########
-  - regex: '; *(TPC-\d+|JAY-TECH) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Jaytech'
-    model_replacement: '$1'
-
-  #########
-  # Jiayu
-  # @ref: http://www.ejiayu.com/en/Product.html
-  #########
-  - regex: '; *(JY-[^;/]+|G[234]S?) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Jiayu'
-    model_replacement: '$1'
-
-  #########
-  # JXD
-  # @ref: http://www.jxd.hk/
-  #########
-  - regex: '; *(JXD)[ _\-]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'JXD'
-    model_replacement: '$2'
-
-  #########
-  # Karbonn
-  # @ref: http://www.karbonnmobiles.com/products_tablet.php
-  #########
-  - regex: '; *Karbonn[ _]?([^;/]+) *(?:Build|;)'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Karbonn'
-    model_replacement: '$1'
-  - regex: '; *([^;]+) Build/Karbonn'
-    device_replacement: '$1'
-    brand_replacement: 'Karbonn'
-    model_replacement: '$1'
-  - regex: '; *(A11|A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2|Titanium S\d) +Build'
-    device_replacement: '$1'
-    brand_replacement: 'Karbonn'
-    model_replacement: '$1'
-
-  #########
-  # KDDI (Operator Branded Device)
-  # @ref: http://www.ipentec.com/document/document.aspx?page=android-useragent
-  #########
-  - regex: '; *(IS01|IS03|IS05|IS\d{2}SH) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sharp'
-    model_replacement: '$1'
-  - regex: '; *(IS04) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Regza'
-    model_replacement: '$1'
-  - regex: '; *(IS06|IS\d{2}PT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Pantech'
-    model_replacement: '$1'
-  - regex: '; *(IS11S) Build'
-    device_replacement: '$1'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: 'Xperia Acro'
-  - regex: '; *(IS11CA) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Casio'
-    model_replacement: 'GzOne $1'
-  - regex: '; *(IS11LG) Build'
-    device_replacement: '$1'
-    brand_replacement: 'LG'
-    model_replacement: 'Optimus X'
-  - regex: '; *(IS11N) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Medias'
-    model_replacement: '$1'
-  - regex: '; *(IS11PT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Pantech'
-    model_replacement: 'MIRACH'
-  - regex: '; *(IS12F) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Fujitsu'
-    model_replacement: 'Arrows ES'
-  # @ref: https://ja.wikipedia.org/wiki/IS12M
-  - regex: '; *(IS12M) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Motorola'
-    model_replacement: 'XT909'
-  - regex: '; *(IS12S) Build'
-    device_replacement: '$1'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: 'Xperia Acro HD'
-  - regex: '; *(ISW11F) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Fujitsu'
-    model_replacement: 'Arrowz Z'
-  - regex: '; *(ISW11HT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'EVO'
-  - regex: '; *(ISW11K) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Kyocera'
-    model_replacement: 'DIGNO'
-  - regex: '; *(ISW11M) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Motorola'
-    model_replacement: 'Photon'
-  - regex: '; *(ISW11SC) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Samsung'
-    model_replacement: 'GALAXY S II WiMAX'
-  - regex: '; *(ISW12HT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'EVO 3D'
-  - regex: '; *(ISW13HT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'J'
-  - regex: '; *(ISW?[0-9]{2}[A-Z]{0,2}) Build'
-    device_replacement: '$1'
-    brand_replacement: 'KDDI'
-    model_replacement: '$1'
-  - regex: '; *(INFOBAR [^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'KDDI'
-    model_replacement: '$1'
-
-  #########
-  # Kingcom
-  # @ref: http://www.e-kingcom.com
-  #########
-  - regex: '; *(JOYPAD|Joypad)[ _]([^;/]+) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Kingcom'
-    model_replacement: '$1 $2'
-
-  #########
-  # Kobo
-  # @ref: https://en.wikipedia.org/wiki/Kobo_Inc.
-  # @ref: http://www.kobo.com/devices#tablets
-  #########
-  - regex: '; *(Vox|VOX|Arc|K080) Build/'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Kobo'
-    model_replacement: '$1'
-  - regex: '\b(Kobo Touch)\b'
-    device_replacement: '$1'
-    brand_replacement: 'Kobo'
-    model_replacement: '$1'
-
-  #########
-  # K-Touch
-  # @ref: ??
-  #########
-  - regex: '; *(K-Touch)[ _]([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Ktouch'
-    model_replacement: '$2'
-
-  #########
-  # KT Tech
-  # @ref: http://www.kttech.co.kr
-  #########
-  - regex: '; *((?:EV|KM)-S\d+[A-Z]?) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'KTtech'
-    model_replacement: '$1'
-
-  #########
-  # Kyocera
-  # @ref: http://www.android.com/devices/?country=all&m=kyocera
-  #########
-  - regex: '; *(Zio|Hydro|Torque|Event|EVENT|Echo|Milano|Rise|URBANO PROGRESSO|WX04K|WX06K|WX10K|KYL21|101K|C5[12]\d{2}) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Kyocera'
-    model_replacement: '$1'
-
-  #########
-  # Lava
-  # @ref: http://www.lavamobiles.com/
-  #########
-  - regex: '; *(?:LAVA[ _])?IRIS[ _\-]?([^/;\)]+) *(?:;|\)|Build)'
-    regex_flag: 'i'
-    device_replacement: 'Iris $1'
-    brand_replacement: 'Lava'
-    model_replacement: 'Iris $1'
-  - regex: '; *LAVA[ _]([^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Lava'
-    model_replacement: '$1'
-
-  #########
-  # Lemon
-  # @ref: http://www.lemonmobiles.com/products.php?type=1
-  #########
-  - regex: '; *(?:(Aspire A1)|(?:LEMON|Lemon)[ _]([^;/]+))_? Build'
-    device_replacement: 'Lemon $1$2'
-    brand_replacement: 'Lemon'
-    model_replacement: '$1$2'
-
-  #########
-  # Lenco
-  # @ref: http://www.lenco.com/c/tablets/
-  #########
-  - regex: '; *(TAB-1012) Build/'
-    device_replacement: 'Lenco $1'
-    brand_replacement: 'Lenco'
-    model_replacement: '$1'
-  - regex: '; Lenco ([^;/]+) Build/'
-    device_replacement: 'Lenco $1'
-    brand_replacement: 'Lenco'
-    model_replacement: '$1'
-
-  #########
-  # Lenovo
-  # @ref: http://support.lenovo.com/en_GB/downloads/default.page?#
-  #########
-  - regex: '; *(A1_07|A2107A-H|S2005A-H|S1-37AH0) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1'
-  - regex: '; *(Idea[Tp]ab)[ _]([^;/]+);? Build'
-    device_replacement: 'Lenovo $1 $2'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1 $2'
-  - regex: '; *(Idea(?:Tab|pad)) ?([^;/]+) Build'
-    device_replacement: 'Lenovo $1 $2'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1 $2'
-  - regex: '; *(ThinkPad) ?(Tablet) Build/'
-    device_replacement: 'Lenovo $1 $2'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1 $2'
-  - regex: '; *(?:LNV-)?(?:=?[Ll]enovo[ _\-]?|LENOVO[ _])+(.+?)(?:Build|[;/\)])'
-    device_replacement: 'Lenovo $1'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1'
-  - regex: '[;,] (?:Vodafone )?(SmartTab) ?(II) ?(\d+) Build/'
-    device_replacement: 'Lenovo $1 $2 $3'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1 $2 $3'
-  - regex: '; *(?:Ideapad )?K1 Build/'
-    device_replacement: 'Lenovo Ideapad K1'
-    brand_replacement: 'Lenovo'
-    model_replacement: 'Ideapad K1'
-  - regex: '; *(3GC101|3GW10[01]|A390) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1'
-  - regex: '\b(?:Lenovo|LENOVO)+[ _\-]?([^,;:/ ]+)'
-    device_replacement: 'Lenovo $1'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1'
-
-  #########
-  # Lexibook
-  # @ref: http://www.lexibook.com/fr
-  #########
-  - regex: '; *(MFC\d+)[A-Z]{2}([^;,/]*),? Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Lexibook'
-    model_replacement: '$1$2'
-
-  #########
-  # LG
-  # @ref: http://www.lg.com/uk/mobile
-  #########
-  - regex: '; *(E[34][0-9]{2}|LS[6-8][0-9]{2}|VS[6-9][0-9]+[^;/]+|Nexus 4|Nexus 5X?|GT540f?|Optimus (?:2X|G|4X HD)|OptimusX4HD) *(?:Build|;)'
-    device_replacement: '$1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-  - regex: '[;:] *(L-\d+[A-Z]|LGL\d+[A-Z]?)(?:/V\d+)? *(?:Build|[;\)])'
-    device_replacement: '$1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-  - regex: '; *(LG-)([A-Z]{1,2}\d{2,}[^,;/\)\(]*?)(?:Build| V\d+|[,;/\)\(]|$)'
-    device_replacement: '$1$2'
-    brand_replacement: 'LG'
-    model_replacement: '$2'
-  - regex: '; *(LG[ \-]|LG)([^;/]+)[;/]? Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'LG'
-    model_replacement: '$2'
-  - regex: '^(LG)-([^;/]+)/ Mozilla/.*; Android'
-    device_replacement: '$1 $2'
-    brand_replacement: 'LG'
-    model_replacement: '$2'
-  - regex: '(Web0S); Linux/(SmartTV)'
-    device_replacement: 'LG $1 $2'
-    brand_replacement: 'LG'
-    model_replacement: '$1 $2'
-
-  #########
-  # Malata
-  # @ref: http://www.malata.com/en/products.aspx?classid=680
-  #########
-  - regex: '; *((?:SMB|smb)[^;/]+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Malata'
-    model_replacement: '$1'
-  - regex: '; *(?:Malata|MALATA) ([^;/]+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Malata'
-    model_replacement: '$1'
-
-  #########
-  # Manta
-  # @ref: http://www.manta.com.pl/en
-  #########
-  - regex: '; *(MS[45][0-9]{3}|MID0[568][NS]?|MID[1-9]|MID[78]0[1-9]|MID970[1-9]|MID100[1-9]) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Manta'
-    model_replacement: '$1'
-
-  #########
-  # Match
-  # @ref: http://www.match.net.cn/products.asp
-  #########
-  - regex: '; *(M1052|M806|M9000|M9100|M9701|MID100|MID120|MID125|MID130|MID135|MID140|MID701|MID710|MID713|MID727|MID728|MID731|MID732|MID733|MID735|MID736|MID737|MID760|MID800|MID810|MID820|MID830|MID833|MID835|MID860|MID900|MID930|MID933|MID960|MID980) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Match'
-    model_replacement: '$1'
-
-  #########
-  # Maxx
-  # @ref: http://www.maxxmobile.in/
-  # @models: Maxx MSD7-Play, Maxx MX245+ Trance, Maxx AX8 Race, Maxx MSD7 3G- AX50, Maxx Genx Droid 7 - AX40, Maxx AX5 Duo,
-  #   Maxx AX3 Duo, Maxx AX3, Maxx AX8 Note II (Note 2), Maxx AX8 Note I, Maxx AX8, Maxx AX5 Plus, Maxx MSD7 Smarty,
-  #   Maxx AX9Z Race,
-  #   Maxx MT150, Maxx MQ601, Maxx M2020, Maxx Sleek MX463neo, Maxx MX525, Maxx MX192-Tune, Maxx Genx Droid 7 AX353,
-  # @note: Need more User-Agents!!!
-  #########
-  - regex: '; *(GenxDroid7|MSD7.*|AX\d.*|Tab 701|Tab 722) Build/'
-    device_replacement: 'Maxx $1'
-    brand_replacement: 'Maxx'
-    model_replacement: '$1'
-
-  #########
-  # Mediacom
-  # @ref: http://www.mediacomeurope.it/
-  #########
-  - regex: '; *(M-PP[^;/]+|PhonePad ?\d{2,}[^;/]+) Build'
-    device_replacement: 'Mediacom $1'
-    brand_replacement: 'Mediacom'
-    model_replacement: '$1'
-  - regex: '; *(M-MP[^;/]+|SmartPad ?\d{2,}[^;/]+) Build'
-    device_replacement: 'Mediacom $1'
-    brand_replacement: 'Mediacom'
-    model_replacement: '$1'
-
-  #########
-  # Medion
-  # @ref: http://www.medion.com/en/
-  #########
-  - regex: '; *(?:MD_)?LIFETAB[ _]([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: 'Medion Lifetab $1'
-    brand_replacement: 'Medion'
-    model_replacement: 'Lifetab $1'
-  - regex: '; *MEDION ([^;/]+) Build'
-    device_replacement: 'Medion $1'
-    brand_replacement: 'Medion'
-    model_replacement: '$1'
-
-  #########
-  # Meizu
-  # @ref: http://www.meizu.com
-  #########
-  - regex: '; *(M030|M031|M035|M040|M065|m9) Build'
-    device_replacement: 'Meizu $1'
-    brand_replacement: 'Meizu'
-    model_replacement: '$1'
-  - regex: '; *(?:meizu_|MEIZU )(.+?) *(?:Build|[;\)])'
-    device_replacement: 'Meizu $1'
-    brand_replacement: 'Meizu'
-    model_replacement: '$1'
-
-  #########
-  # Micromax
-  # @ref: http://www.micromaxinfo.com
-  #########
-  - regex: '; *(?:Micromax[ _](A111|A240)|(A111|A240)) Build'
-    regex_flag: 'i'
-    device_replacement: 'Micromax $1$2'
-    brand_replacement: 'Micromax'
-    model_replacement: '$1$2'
-  - regex: '; *Micromax[ _](A\d{2,3}[^;/]*) Build'
-    regex_flag: 'i'
-    device_replacement: 'Micromax $1'
-    brand_replacement: 'Micromax'
-    model_replacement: '$1'
-  # be carefull here with Acer e.g. A500
-  - regex: '; *(A\d{2}|A[12]\d{2}|A90S|A110Q) Build'
-    regex_flag: 'i'
-    device_replacement: 'Micromax $1'
-    brand_replacement: 'Micromax'
-    model_replacement: '$1'
-  - regex: '; *Micromax[ _](P\d{3}[^;/]*) Build'
-    regex_flag: 'i'
-    device_replacement: 'Micromax $1'
-    brand_replacement: 'Micromax'
-    model_replacement: '$1'
-  - regex: '; *(P\d{3}|P\d{3}\(Funbook\)) Build'
-    regex_flag: 'i'
-    device_replacement: 'Micromax $1'
-    brand_replacement: 'Micromax'
-    model_replacement: '$1'
-
-  #########
-  # Mito
-  # @ref: http://new.mitomobile.com/
-  #########
-  - regex: '; *(MITO)[ _\-]?([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Mito'
-    model_replacement: '$2'
-
-  #########
-  # Mobistel
-  # @ref: http://www.mobistel.com/
-  #########
-  - regex: '; *(Cynus)[ _](F5|T\d|.+?) *(?:Build|[;/\)])'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Mobistel'
-    model_replacement: '$1 $2'
-
-  #########
-  # Modecom
-  # @ref: http://www.modecom.eu/tablets/portal/
-  #########
-  - regex: '; *(MODECOM )?(FreeTab) ?([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1$2 $3'
-    brand_replacement: 'Modecom'
-    model_replacement: '$2 $3'
-  - regex: '; *(MODECOM )([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Modecom'
-    model_replacement: '$2'
-
-  #########
-  # Motorola
-  # @ref: http://www.motorola.com/us/shop-all-mobile-phones/
-  #########
-  - regex: '; *(MZ\d{3}\+?|MZ\d{3} 4G|Xoom|XOOM[^;/]*) Build'
-    device_replacement: 'Motorola $1'
-    brand_replacement: 'Motorola'
-    model_replacement: '$1'
-  - regex: '; *(Milestone )(XT[^;/]*) Build'
-    device_replacement: 'Motorola $1$2'
-    brand_replacement: 'Motorola'
-    model_replacement: '$2'
-  - regex: '; *(Motoroi ?x|Droid X|DROIDX) Build'
-    regex_flag: 'i'
-    device_replacement: 'Motorola $1'
-    brand_replacement: 'Motorola'
-    model_replacement: 'DROID X'
-  - regex: '; *(Droid[^;/]*|DROID[^;/]*|Milestone[^;/]*|Photon|Triumph|Devour|Titanium) Build'
-    device_replacement: 'Motorola $1'
-    brand_replacement: 'Motorola'
-    model_replacement: '$1'
-  - regex: '; *(A555|A85[34][^;/]*|A95[356]|ME[58]\d{2}\+?|ME600|ME632|ME722|MB\d{3}\+?|MT680|MT710|MT870|MT887|MT917|WX435|WX453|WX44[25]|XT\d{3,4}[A-Z\+]*|CL[iI]Q|CL[iI]Q XT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Motorola'
-    model_replacement: '$1'
-  - regex: '; *(Motorola MOT-|Motorola[ _\-]|MOT\-?)([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Motorola'
-    model_replacement: '$2'
-  - regex: '; *(Moto[_ ]?|MOT\-)([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Motorola'
-    model_replacement: '$2'
-
-  #########
-  # MpMan
-  # @ref: http://www.mpmaneurope.com
-  #########
-  - regex: '; *((?:MP[DQ]C|MPG\d{1,4}|MP\d{3,4}|MID(?:(?:10[234]|114|43|7[247]|8[24]|7)C|8[01]1))[^;/]*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Mpman'
-    model_replacement: '$1'
-
-  #########
-  # MSI
-  # @ref: http://www.msi.com/product/windpad/
-  #########
-  - regex: '; *(?:MSI[ _])?(Primo\d+|Enjoy[ _\-][^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'Msi'
-    model_replacement: '$1'
-
-  #########
-  # Multilaser
-  # http://www.multilaser.com.br/listagem_produtos.php?cat=5
-  #########
-  - regex: '; *Multilaser[ _]([^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Multilaser'
-    model_replacement: '$1'
-
-  #########
-  # MyPhone
-  # @ref: http://myphone.com.ph/
-  #########
-  - regex: '; *(My)[_]?(Pad)[ _]([^;/]+) Build'
-    device_replacement: '$1$2 $3'
-    brand_replacement: 'MyPhone'
-    model_replacement: '$1$2 $3'
-  - regex: '; *(My)\|?(Phone)[ _]([^;/]+) Build'
-    device_replacement: '$1$2 $3'
-    brand_replacement: 'MyPhone'
-    model_replacement: '$3'
-  - regex: '; *(A\d+)[ _](Duo)? Build'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'MyPhone'
-    model_replacement: '$1 $2'
-
-  #########
-  # Mytab
-  # @ref: http://www.mytab.eu/en/category/mytab-products/
-  #########
-  - regex: '; *(myTab[^;/]*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Mytab'
-    model_replacement: '$1'
-
-  #########
-  # Nabi
-  # @ref: https://www.nabitablet.com
-  #########
-  - regex: '; *(NABI2?-)([^;/]+) Build/'
-    device_replacement: '$1$2'
-    brand_replacement: 'Nabi'
-    model_replacement: '$2'
-
-  #########
-  # Nec Medias
-  # @ref: http://www.n-keitai.com/
-  #########
-  - regex: '; *(N-\d+[CDE]) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Nec'
-    model_replacement: '$1'
-  - regex: '; ?(NEC-)(.*) Build/'
-    device_replacement: '$1$2'
-    brand_replacement: 'Nec'
-    model_replacement: '$2'
-  - regex: '; *(LT-NA7) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Nec'
-    model_replacement: 'Lifetouch Note'
-
-  #########
-  # Nextbook
-  # @ref: http://nextbookusa.com
-  #########
-  - regex: '; *(NXM\d+[A-z0-9_]*|Next\d[A-z0-9_ \-]*|NEXT\d[A-z0-9_ \-]*|Nextbook [A-z0-9_ ]*|DATAM803HC|M805)(?: Build|[\);])'
-    device_replacement: '$1'
-    brand_replacement: 'Nextbook'
-    model_replacement: '$1'
-
-  #########
-  # Nokia
-  # @ref: http://www.nokia.com
-  #########
-  - regex: '; *(Nokia)([ _\-]*)([^;/]*) Build'
-    regex_flag: 'i'
-    device_replacement: '$1$2$3'
-    brand_replacement: 'Nokia'
-    model_replacement: '$3'
-
-  #########
-  # Nook
-  # @ref:
-  # TODO nook browser/1.0
-  #########
-  - regex: '; *(Nook ?|Barnes & Noble Nook |BN )([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Nook'
-    model_replacement: '$2'
-  - regex: '; *(NOOK )?(BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Nook'
-    model_replacement: '$2'
-  - regex: '; Build/(Nook)'
-    device_replacement: '$1'
-    brand_replacement: 'Nook'
-    model_replacement: 'Tablet'
-
-  #########
-  # Olivetti
-  # @ref: http://www.olivetti.de/EN/Page/t02/view_html?idp=348
-  #########
-  - regex: '; *(OP110|OliPad[^;/]+) Build'
-    device_replacement: 'Olivetti $1'
-    brand_replacement: 'Olivetti'
-    model_replacement: '$1'
-
-  #########
-  # Omega
-  # @ref: http://omega-technology.eu/en/produkty/346/tablets
-  # @note: MID tablets might get matched by CobyKyros first
-  # @models: (T107|MID(?:700[2-5]|7031|7108|7132|750[02]|8001|8500|9001|971[12])
-  #########
-  - regex: '; *OMEGA[ _\-](MID[^;/]+) Build'
-    device_replacement: 'Omega $1'
-    brand_replacement: 'Omega'
-    model_replacement: '$1'
-  - regex: '^(MID7500|MID\d+) Mozilla/5\.0 \(iPad;'
-    device_replacement: 'Omega $1'
-    brand_replacement: 'Omega'
-    model_replacement: '$1'
-
-  #########
-  # OpenPeak
-  # @ref: https://support.google.com/googleplay/answer/1727131?hl=en
-  #########
-  - regex: '; *((?:CIUS|cius)[^;/]*) Build'
-    device_replacement: 'Openpeak $1'
-    brand_replacement: 'Openpeak'
-    model_replacement: '$1'
-
-  #########
-  # Oppo
-  # @ref: http://en.oppo.com/products/
-  #########
-  - regex: '; *(Find ?(?:5|7a)|R8[012]\d{1,2}|T703\d{0,1}|U70\d{1,2}T?|X90\d{1,2}) Build'
-    device_replacement: 'Oppo $1'
-    brand_replacement: 'Oppo'
-    model_replacement: '$1'
-  - regex: '; *OPPO ?([^;/]+) Build/'
-    device_replacement: 'Oppo $1'
-    brand_replacement: 'Oppo'
-    model_replacement: '$1'
-
-  #########
-  # Odys
-  # @ref: http://odys.de
-  #########
-  - regex: '; *(?:Odys\-|ODYS\-|ODYS )([^;/]+) Build'
-    device_replacement: 'Odys $1'
-    brand_replacement: 'Odys'
-    model_replacement: '$1'
-  - regex: '; *(SELECT) ?(7) Build'
-    device_replacement: 'Odys $1 $2'
-    brand_replacement: 'Odys'
-    model_replacement: '$1 $2'
-  - regex: '; *(PEDI)_(PLUS)_(W) Build'
-    device_replacement: 'Odys $1 $2 $3'
-    brand_replacement: 'Odys'
-    model_replacement: '$1 $2 $3'
-  # Weltbild - Tablet PC 4 = Cat Phoenix = Odys Tablet PC 4?
-  - regex: '; *(AEON|BRAVIO|FUSION|FUSION2IN1|Genio|EOS10|IEOS[^;/]*|IRON|Loox|LOOX|LOOX Plus|Motion|NOON|NOON_PRO|NEXT|OPOS|PEDI[^;/]*|PRIME[^;/]*|STUDYTAB|TABLO|Tablet-PC-4|UNO_X8|XELIO[^;/]*|Xelio ?\d+ ?[Pp]ro|XENO10|XPRESS PRO) Build'
-    device_replacement: 'Odys $1'
-    brand_replacement: 'Odys'
-    model_replacement: '$1'
-
-  #########
-  # OnePlus
-  # @ref https://oneplus.net/
-  #########
-  - regex: '; (ONE [a-zA-Z]\d+) Build/'
-    device_replacement: 'OnePlus $1'
-    brand_replacement: 'OnePlus'
-    model_replacement: '$1'
-  - regex: '; (ONEPLUS [a-zA-Z]\d+) Build/'
-    device_replacement: 'OnePlus $1'
-    brand_replacement: 'OnePlus'
-    model_replacement: '$1'
-
-  #########
-  # Orion
-  # @ref: http://www.orion.ua/en/products/computer-products/tablet-pcs.html
-  #########
-  - regex: '; *(TP-\d+) Build/'
-    device_replacement: 'Orion $1'
-    brand_replacement: 'Orion'
-    model_replacement: '$1'
-
-  #########
-  # PackardBell
-  # @ref: http://www.packardbell.com/pb/en/AE/content/productgroup/tablets
-  #########
-  - regex: '; *(G100W?) Build/'
-    device_replacement: 'PackardBell $1'
-    brand_replacement: 'PackardBell'
-    model_replacement: '$1'
-
-  #########
-  # Panasonic
-  # @ref: http://panasonic.jp/mobile/
-  # @models: T11, T21, T31, P11, P51, Eluga Power, Eluga DL1
-  # @models: (tab) Toughpad FZ-A1, Toughpad JT-B1
-  #########
-  - regex: '; *(Panasonic)[_ ]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  # Toughpad
-  - regex: '; *(FZ-A1B|JT-B1) Build'
-    device_replacement: 'Panasonic $1'
-    brand_replacement: 'Panasonic'
-    model_replacement: '$1'
-  # Eluga Power
-  - regex: '; *(dL1|DL1) Build'
-    device_replacement: 'Panasonic $1'
-    brand_replacement: 'Panasonic'
-    model_replacement: '$1'
-
-  #########
-  # Pantech
-  # @href: http://www.pantech.co.kr/en/prod/prodList.do?gbrand=PANTECH
-  # @href: http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA
-  # @models: ADR8995, ADR910L, ADR930VW, C790, CDM8992, CDM8999, IS06, IS11PT, P2000, P2020, P2030, P4100, P5000, P6010, P6020, P6030, P7000, P7040, P8000, P8010, P9020, P9050, P9060, P9070, P9090, PT001, PT002, PT003, TXT8040, TXT8045, VEGA PTL21
-  #########
-  - regex: '; *(SKY[ _])?(IM\-[AT]\d{3}[^;/]+).* Build/'
-    device_replacement: 'Pantech $1$2'
-    brand_replacement: 'Pantech'
-    model_replacement: '$1$2'
-  - regex: '; *((?:ADR8995|ADR910L|ADR930L|ADR930VW|PTL21|P8000)(?: 4G)?) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Pantech'
-    model_replacement: '$1'
-  - regex: '; *Pantech([^;/]+).* Build/'
-    device_replacement: 'Pantech $1'
-    brand_replacement: 'Pantech'
-    model_replacement: '$1'
-
-  #########
-  # Papayre
-  # @ref: http://grammata.es/
-  #########
-  - regex: '; *(papyre)[ _\-]([^;/]+) Build/'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Papyre'
-    model_replacement: '$2'
-
-  #########
-  # Pearl
-  # @ref: http://www.pearl.de/c-1540.shtml
-  #########
-  - regex: '; *(?:Touchlet )?(X10\.[^;/]+) Build/'
-    device_replacement: 'Pearl $1'
-    brand_replacement: 'Pearl'
-    model_replacement: '$1'
-
-  #########
-  # Phicomm
-  # @ref: http://www.phicomm.com.cn/
-  #########
-  - regex: '; PHICOMM (i800) Build/'
-    device_replacement: 'Phicomm $1'
-    brand_replacement: 'Phicomm'
-    model_replacement: '$1'
-  - regex: '; PHICOMM ([^;/]+) Build/'
-    device_replacement: 'Phicomm $1'
-    brand_replacement: 'Phicomm'
-    model_replacement: '$1'
-  - regex: '; *(FWS\d{3}[^;/]+) Build/'
-    device_replacement: 'Phicomm $1'
-    brand_replacement: 'Phicomm'
-    model_replacement: '$1'
-
-  #########
-  # Philips
-  # @ref: http://www.support.philips.com/support/catalog/products.jsp?_dyncharset=UTF-8&country=&categoryid=MOBILE_PHONES_SMART_SU_CN_CARE&userLanguage=en&navCount=2&groupId=PC_PRODUCTS_AND_PHONES_GR_CN_CARE&catalogType=&navAction=push&userCountry=cn&title=Smartphones&cateId=MOBILE_PHONES_CA_CN_CARE
-  # @TODO: Philips Tablets User-Agents missing!
-  # @ref: http://www.support.philips.com/support/catalog/products.jsp?_dyncharset=UTF-8&country=&categoryid=ENTERTAINMENT_TABLETS_SU_CN_CARE&userLanguage=en&navCount=0&groupId=&catalogType=&navAction=push&userCountry=cn&title=Entertainment+Tablets&cateId=TABLETS_CA_CN_CARE
-  #########
-  # @note: this a best guess according to available philips models. Need more User-Agents
-  - regex: '; *(D633|D822|D833|T539|T939|V726|W335|W336|W337|W3568|W536|W5510|W626|W632|W6350|W6360|W6500|W732|W736|W737|W7376|W820|W832|W8355|W8500|W8510|W930) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Philips'
-    model_replacement: '$1'
-  - regex: '; *(?:Philips|PHILIPS)[ _]([^;/]+) Build'
-    device_replacement: 'Philips $1'
-    brand_replacement: 'Philips'
-    model_replacement: '$1'
-
-  #########
-  # Pipo
-  # @ref: http://www.pipo.cn/En/
-  #########
-  - regex: 'Android 4\..*; *(M[12356789]|U[12368]|S[123])\ ?(pro)? Build'
-    device_replacement: 'Pipo $1$2'
-    brand_replacement: 'Pipo'
-    model_replacement: '$1$2'
-
-  #########
-  # Ployer
-  # @ref: http://en.ployer.cn/
-  #########
-  - regex: '; *(MOMO[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Ployer'
-    model_replacement: '$1'
-
-  #########
-  # Polaroid/ Acho
-  # @ref: http://polaroidstore.com/store/start.asp?category_id=382&category_id2=0&order=title&filter1=&filter2=&filter3=&view=all
-  #########
-  - regex: '; *(?:Polaroid[ _])?((?:MIDC\d{3,}|PMID\d{2,}|PTAB\d{3,})[^;/]*)(\/[^;/]*)? Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Polaroid'
-    model_replacement: '$1'
-  - regex: '; *(?:Polaroid )(Tablet) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Polaroid'
-    model_replacement: '$1'
-
-  #########
-  # Pomp
-  # @ref: http://pompmobileshop.com/
-  #########
-  #~ TODO
-  - regex: '; *(POMP)[ _\-](.+?) *(?:Build|[;/\)])'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Pomp'
-    model_replacement: '$2'
-
-  #########
-  # Positivo
-  # @ref: http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
-  #########
-  - regex: '; *(TB07STA|TB10STA|TB07FTA|TB10FTA) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Positivo'
-    model_replacement: '$1'
-  - regex: '; *(?:Positivo )?((?:YPY|Ypy)[^;/]+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Positivo'
-    model_replacement: '$1'
-
-  #########
-  # POV
-  # @ref: http://www.pointofview-online.com/default2.php
-  # @TODO: Smartphone Models MOB-3515, MOB-5045-B missing
-  #########
-  - regex: '; *(MOB-[^;/]+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'POV'
-    model_replacement: '$1'
-  - regex: '; *POV[ _\-]([^;/]+) Build/'
-    device_replacement: 'POV $1'
-    brand_replacement: 'POV'
-    model_replacement: '$1'
-  - regex: '; *((?:TAB-PLAYTAB|TAB-PROTAB|PROTAB|PlayTabPro|Mobii[ _\-]|TAB-P)[^;/]*) Build/'
-    device_replacement: 'POV $1'
-    brand_replacement: 'POV'
-    model_replacement: '$1'
-
-  #########
-  # Prestigio
-  # @ref: http://www.prestigio.com/catalogue/MultiPhones
-  # @ref: http://www.prestigio.com/catalogue/MultiPads
-  #########
-  - regex: '; *(?:Prestigio )?((?:PAP|PMP)\d[^;/]+) Build/'
-    device_replacement: 'Prestigio $1'
-    brand_replacement: 'Prestigio'
-    model_replacement: '$1'
-
-  #########
-  # Proscan
-  # @ref: http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
-  #########
-  - regex: '; *(PLT[0-9]{4}.*) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Proscan'
-    model_replacement: '$1'
-
-  #########
-  # QMobile
-  # @ref: http://www.qmobile.com.pk/
-  #########
-  - regex: '; *(A2|A5|A8|A900)_?(Classic)? Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Qmobile'
-    model_replacement: '$1 $2'
-  - regex: '; *(Q[Mm]obile)_([^_]+)_([^_]+) Build'
-    device_replacement: 'Qmobile $2 $3'
-    brand_replacement: 'Qmobile'
-    model_replacement: '$2 $3'
-  - regex: '; *(Q\-?[Mm]obile)[_ ](A[^;/]+) Build'
-    device_replacement: 'Qmobile $2'
-    brand_replacement: 'Qmobile'
-    model_replacement: '$2'
-
-  #########
-  # Qmobilevn
-  # @ref: http://qmobile.vn/san-pham.html
-  #########
-  - regex: '; *(Q\-Smart)[ _]([^;/]+) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Qmobilevn'
-    model_replacement: '$2'
-  - regex: '; *(Q\-?[Mm]obile)[ _\-](S[^;/]+) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Qmobilevn'
-    model_replacement: '$2'
-
-  #########
-  # Quanta
-  # @ref: ?
-  #########
-  - regex: '; *(TA1013) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Quanta'
-    model_replacement: '$1'
-
-  #########
-  # RCA
-  # @ref: http://rcamobilephone.com/
-  #########
-  - regex: '; (RCT\w+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'RCA'
-    model_replacement: '$1'
-  - regex: '; RCA (\w+) Build/'
-    device_replacement: 'RCA $1'
-    brand_replacement: 'RCA'
-    model_replacement: '$1'
-
-  #########
-  # Rockchip
-  # @ref: http://www.rock-chips.com/a/cn/product/index.html
-  # @note: manufacturer sells chipsets - I assume that these UAs are dev-boards
-  #########
-  - regex: '; *(RK\d+),? Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Rockchip'
-    model_replacement: '$1'
-  - regex: ' Build/(RK\d+)'
-    device_replacement: '$1'
-    brand_replacement: 'Rockchip'
-    model_replacement: '$1'
-
-  #########
-  # Samsung Android Devices
-  # @ref: http://www.samsung.com/us/mobile/cell-phones/all-products
-  #########
-  - regex: '; *(SAMSUNG |Samsung )?((?:Galaxy (?:Note II|S\d)|GT-I9082|GT-I9205|GT-N7\d{3}|SM-N9005)[^;/]*)\/?[^;/]* Build/'
-    device_replacement: 'Samsung $1$2'
-    brand_replacement: 'Samsung'
-    model_replacement: '$2'
-  - regex: '; *(Google )?(Nexus [Ss](?: 4G)?) Build/'
-    device_replacement: 'Samsung $1$2'
-    brand_replacement: 'Samsung'
-    model_replacement: '$2'
-  - regex: '; *(SAMSUNG |Samsung )([^\/]*)\/[^ ]* Build/'
-    device_replacement: 'Samsung $2'
-    brand_replacement: 'Samsung'
-    model_replacement: '$2'
-  - regex: '; *(Galaxy(?: Ace| Nexus| S ?II+|Nexus S| with MCR 1.2| Mini Plus 4G)?) Build/'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  - regex: '; *(SAMSUNG[ _\-] *)+([^;/]+) Build'
-    device_replacement: 'Samsung $2'
-    brand_replacement: 'Samsung'
-    model_replacement: '$2'
-  - regex: '; *(SAMSUNG-)?(GT\-[BINPS]\d{4}[^\/]*)(\/[^ ]*) Build'
-    device_replacement: 'Samsung $1$2$3'
-    brand_replacement: 'Samsung'
-    model_replacement: '$2'
-  - regex: '(?:; *|^)((?:GT\-[BIiNPS]\d{4}|I9\d{2}0[A-Za-z\+]?\b)[^;/\)]*?)(?:Build|Linux|MIUI|[;/\)])'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  - regex: '; (SAMSUNG-)([A-Za-z0-9\-]+).* Build/'
-    device_replacement: 'Samsung $1$2'
-    brand_replacement: 'Samsung'
-    model_replacement: '$2'
-  - regex: '; *((?:SCH|SGH|SHV|SHW|SPH|SC|SM)\-[A-Za-z0-9 ]+)(/?[^ ]*)? Build'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  - regex: ' ((?:SCH)\-[A-Za-z0-9 ]+)(/?[^ ]*)? Build'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  - regex: '; *(Behold ?(?:2|II)|YP\-G[^;/]+|EK-GC100|SCL21|I9300) Build'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-
-  #########
-  # Sharp
-  # @ref: http://www.sharp-phone.com/en/index.html
-  # @ref: http://www.android.com/devices/?country=all&m=sharp
-  #########
-  - regex: '; *(SH\-?\d\d[^;/]+|SBM\d[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sharp'
-    model_replacement: '$1'
-  - regex: '; *(SHARP[ -])([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'Sharp'
-    model_replacement: '$2'
-
-  #########
-  # Simvalley
-  # @ref: http://www.simvalley-mobile.de/
-  #########
-  - regex: '; *(SPX[_\-]\d[^;/]*) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Simvalley'
-    model_replacement: '$1'
-  - regex: '; *(SX7\-PEARL\.GmbH) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Simvalley'
-    model_replacement: '$1'
-  - regex: '; *(SP[T]?\-\d{2}[^;/]*) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Simvalley'
-    model_replacement: '$1'
-
-  #########
-  # SK Telesys
-  # @ref: http://www.sk-w.com/phone/phone_list.jsp
-  # @ref: http://www.android.com/devices/?country=all&m=sk-telesys
-  #########
-  - regex: '; *(SK\-.*) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'SKtelesys'
-    model_replacement: '$1'
-
-  #########
-  # Skytex
-  # @ref: http://skytex.com/android
-  #########
-  - regex: '; *(?:SKYTEX|SX)-([^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Skytex'
-    model_replacement: '$1'
-  - regex: '; *(IMAGINE [^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Skytex'
-    model_replacement: '$1'
-
-  #########
-  # SmartQ
-  # @ref: http://en.smartdevices.com.cn/Products/
-  # @models: Z8, X7, U7H, U7, T30, T20, Ten3, V5-II, T7-3G, SmartQ5, K7, S7, Q8, T19, Ten2, Ten, R10, T7, R7, V5, V7, SmartQ7
-  #########
-  - regex: '; *(SmartQ) ?([^;/]+) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Smartbitt
-  # @ref: http://www.smartbitt.com/
-  # @missing: SBT Useragents
-  #########
-  - regex: '; *(WF7C|WF10C|SBT[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Smartbitt'
-    model_replacement: '$1'
-
-  #########
-  # Softbank (Operator Branded Devices)
-  # @ref: http://www.ipentec.com/document/document.aspx?page=android-useragent
-  #########
-  - regex: '; *(SBM(?:003SH|005SH|006SH|007SH|102SH)) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sharp'
-    model_replacement: '$1'
-  - regex: '; *(003P|101P|101P11C|102P) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Panasonic'
-    model_replacement: '$1'
-  - regex: '; *(00\dZ) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-  - regex: '; HTC(X06HT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  - regex: '; *(001HT|X06HT) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-  - regex: '; *(201M) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Motorola'
-    model_replacement: 'XT902'
-
-  #########
-  # Trekstor
-  # @ref: http://www.trekstor.co.uk/surftabs-en.html
-  # @note: Must come before SonyEricsson
-  #########
-  - regex: '; *(ST\d{4}.*)Build/ST'
-    device_replacement: 'Trekstor $1'
-    brand_replacement: 'Trekstor'
-    model_replacement: '$1'
-  - regex: '; *(ST\d{4}.*) Build/'
-    device_replacement: 'Trekstor $1'
-    brand_replacement: 'Trekstor'
-    model_replacement: '$1'
-
-  #########
-  # SonyEricsson
-  # @note: Must come before nokia since they also use symbian
-  # @ref: http://www.android.com/devices/?country=all&m=sony-ericssons
-  # @TODO: type!
-  #########
-  # android matchers
-  - regex: '; *(Sony ?Ericsson ?)([^;/]+) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: '$2'
-  - regex: '; *((?:SK|ST|E|X|LT|MK|MT|WT)\d{2}[a-z0-9]*(?:-o)?|R800i|U20i) Build'
-    device_replacement: '$1'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: '$1'
-  # TODO X\d+ is wrong
-  - regex: '; *(Xperia (?:A8|Arc|Acro|Active|Live with Walkman|Mini|Neo|Play|Pro|Ray|X\d+)[^;/]*) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: '$1'
-
-  #########
-  # Sony
-  # @ref: http://www.sonymobile.co.jp/index.html
-  # @ref: http://www.sonymobile.com/global-en/products/phones/
-  # @ref: http://www.sony.jp/tablet/
-  #########
-  - regex: '; Sony (Tablet[^;/]+) Build'
-    device_replacement: 'Sony $1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-  - regex: '; Sony ([^;/]+) Build'
-    device_replacement: 'Sony $1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-  - regex: '; *(Sony)([A-Za-z0-9\-]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  - regex: '; *(Xperia [^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-  - regex: '; *(C(?:1[0-9]|2[0-9]|53|55|6[0-9])[0-9]{2}|D[25]\d{3}|D6[56]\d{2}) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-  - regex: '; *(SGP\d{3}|SGPT\d{2}) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-  - regex: '; *(NW-Z1000Series) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-
-  ##########
-  # Sony PlayStation
-  # @ref: http://playstation.com
-  # The Vita spoofs the Kindle
-  ##########
-  - regex: 'PLAYSTATION 3'
-    device_replacement: 'PlayStation 3'
-    brand_replacement: 'Sony'
-    model_replacement: 'PlayStation 3'
-  - regex: '(PlayStation (?:Portable|Vita|\d+))'
-    device_replacement: '$1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1'
-
-  #########
-  # Spice
-  # @ref: http://www.spicemobilephones.co.in/
-  #########
-  - regex: '; *((?:CSL_Spice|Spice|SPICE|CSL)[ _\-]?)?([Mm][Ii])([ _\-])?(\d{3}[^;/]*) Build/'
-    device_replacement: '$1$2$3$4'
-    brand_replacement: 'Spice'
-    model_replacement: 'Mi$4'
-
-  #########
-  # Sprint (Operator Branded Devices)
-  # @ref:
-  #########
-  - regex: '; *(Sprint )(.+?) *(?:Build|[;/])'
-    device_replacement: '$1$2'
-    brand_replacement: 'Sprint'
-    model_replacement: '$2'
-  - regex: '\b(Sprint)[: ]([^;,/ ]+)'
-    device_replacement: '$1$2'
-    brand_replacement: 'Sprint'
-    model_replacement: '$2'
-
-  #########
-  # Tagi
-  # @ref: ??
-  #########
-  - regex: '; *(TAGI[ ]?)(MID) ?([^;/]+) Build/'
-    device_replacement: '$1$2$3'
-    brand_replacement: 'Tagi'
-    model_replacement: '$2$3'
-
-  #########
-  # Tecmobile
-  # @ref: http://www.tecmobile.com/
-  #########
-  - regex: '; *(Oyster500|Opal 800) Build'
-    device_replacement: 'Tecmobile $1'
-    brand_replacement: 'Tecmobile'
-    model_replacement: '$1'
-
-  #########
-  # Tecno
-  # @ref: www.tecno-mobile.com/‎
-  #########
-  - regex: '; *(TECNO[ _])([^;/]+) Build/'
-    device_replacement: '$1$2'
-    brand_replacement: 'Tecno'
-    model_replacement: '$2'
-
-  #########
-  # Telechips, Techvision evaluation boards
-  # @ref:
-  #########
-  - regex: '; *Android for (Telechips|Techvision) ([^ ]+) '
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Telstra
-  # @ref: http://www.telstra.com.au/home-phone/thub-2/
-  # @ref: https://support.google.com/googleplay/answer/1727131?hl=en
-  #########
-  - regex: '; *(T-Hub2) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Telstra'
-    model_replacement: '$1'
-
-  #########
-  # Terra
-  # @ref: http://www.wortmann.de/
-  #########
-  - regex: '; *(PAD) ?(100[12]) Build/'
-    device_replacement: 'Terra $1$2'
-    brand_replacement: 'Terra'
-    model_replacement: '$1$2'
-
-  #########
-  # Texet
-  # @ref: http://www.texet.ru/tablet/
-  #########
-  - regex: '; *(T[BM]-\d{3}[^;/]+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Texet'
-    model_replacement: '$1'
-
-  #########
-  # Thalia
-  # @ref: http://www.thalia.de/shop/tolino-shine-ereader/show/
-  #########
-  - regex: '; *(tolino [^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Thalia'
-    model_replacement: '$1'
-  - regex: '; *Build/.* (TOLINO_BROWSER)'
-    device_replacement: '$1'
-    brand_replacement: 'Thalia'
-    model_replacement: 'Tolino Shine'
-
-  #########
-  # Thl
-  # @ref: http://en.thl.com.cn/Mobile
-  # @ref: http://thlmobilestore.com
-  #########
-  - regex: '; *(?:CJ[ -])?(ThL|THL)[ -]([^;/]+) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Thl'
-    model_replacement: '$2'
-  - regex: '; *(T100|T200|T5|W100|W200|W8s) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Thl'
-    model_replacement: '$1'
-
-  #########
-  # T-Mobile (Operator Branded Devices)
-  #########
-  # @ref: https://en.wikipedia.org/wiki/HTC_Hero
-  - regex: '; *(T-Mobile[ _]G2[ _]Touch) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'Hero'
-  # @ref: https://en.wikipedia.org/wiki/HTC_Desire_Z
-  - regex: '; *(T-Mobile[ _]G2) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'Desire Z'
-  - regex: '; *(T-Mobile myTouch Q) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Huawei'
-    model_replacement: 'U8730'
-  - regex: '; *(T-Mobile myTouch) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Huawei'
-    model_replacement: 'U8680'
-  - regex: '; *(T-Mobile_Espresso) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'Espresso'
-  - regex: '; *(T-Mobile G1) Build'
-    device_replacement: '$1'
-    brand_replacement: 'HTC'
-    model_replacement: 'Dream'
-  - regex: '\b(T-Mobile ?)?(myTouch)[ _]?([34]G)[ _]?([^\/]*) (?:Mozilla|Build)'
-    device_replacement: '$1$2 $3 $4'
-    brand_replacement: 'HTC'
-    model_replacement: '$2 $3 $4'
-  - regex: '\b(T-Mobile)_([^_]+)_(.*) Build'
-    device_replacement: '$1 $2 $3'
-    brand_replacement: 'Tmobile'
-    model_replacement: '$2 $3'
-  - regex: '\b(T-Mobile)[_ ]?(.*?)Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Tmobile'
-    model_replacement: '$2'
-
-  #########
-  # Tomtec
-  # @ref: http://www.tom-tec.eu/pages/tablets.php
-  #########
-  - regex: ' (ATP[0-9]{4}) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Tomtec'
-    model_replacement: '$1'
-
-  #########
-  # Tooky
-  # @ref: http://www.tookymobile.com/
-  #########
-  - regex: ' *(TOOKY)[ _\-]([^;/]+) ?(?:Build|;)'
-    regex_flag: 'i'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Tooky'
-    model_replacement: '$2'
-
-  #########
-  # Toshiba
-  # @ref: http://www.toshiba.co.jp/
-  # @missing: LT170, Thrive 7, TOSHIBA STB10
-  #########
-  - regex: '\b(TOSHIBA_AC_AND_AZ|TOSHIBA_FOLIO_AND_A|FOLIO_AND_A)'
-    device_replacement: '$1'
-    brand_replacement: 'Toshiba'
-    model_replacement: 'Folio 100'
-  - regex: '; *([Ff]olio ?100) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Toshiba'
-    model_replacement: 'Folio 100'
-  - regex: '; *(AT[0-9]{2,3}(?:\-A|LE\-A|PE\-A|SE|a)?|AT7-A|AT1S0|Hikari-iFrame/WDPF-[^;/]+|THRiVE|Thrive) Build/'
-    device_replacement: 'Toshiba $1'
-    brand_replacement: 'Toshiba'
-    model_replacement: '$1'
-
-  #########
-  # Touchmate
-  # @ref: http://touchmatepc.com/new/
-  #########
-  - regex: '; *(TM-MID\d+[^;/]+|TOUCHMATE|MID-750) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Touchmate'
-    model_replacement: '$1'
-  # @todo: needs verification user-agents missing
-  - regex: '; *(TM-SM\d+[^;/]+) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Touchmate'
-    model_replacement: '$1'
-
-  #########
-  # Treq
-  # @ref: http://www.treq.co.id/product
-  #########
-  - regex: '; *(A10 [Bb]asic2?) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Treq'
-    model_replacement: '$1'
-  - regex: '; *(TREQ[ _\-])([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: '$1$2'
-    brand_replacement: 'Treq'
-    model_replacement: '$2'
-
-  #########
-  # Umeox
-  # @ref: http://umeox.com/
-  # @models: A936|A603|X-5|X-3
-  #########
-  # @todo: guessed markers
-  - regex: '; *(X-?5|X-?3) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Umeox'
-    model_replacement: '$1'
-  # @todo: guessed markers
-  - regex: '; *(A502\+?|A936|A603|X1|X2) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Umeox'
-    model_replacement: '$1'
-
-  #########
-  # Versus
-  # @ref: http://versusuk.com/support.html
-  #########
-  - regex: '(TOUCH(?:TAB|PAD).+?) Build/'
-    regex_flag: 'i'
-    device_replacement: 'Versus $1'
-    brand_replacement: 'Versus'
-    model_replacement: '$1'
-
-  #########
-  # Vertu
-  # @ref: http://www.vertu.com/
-  #########
-  - regex: '(VERTU) ([^;/]+) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Vertu'
-    model_replacement: '$2'
-
-  #########
-  # Videocon
-  # @ref: http://www.videoconmobiles.com
-  #########
-  - regex: '; *(Videocon)[ _\-]([^;/]+) *(?:Build|;)'
-    device_replacement: '$1 $2'
-    brand_replacement: 'Videocon'
-    model_replacement: '$2'
-  - regex: ' (VT\d{2}[A-Za-z]*) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Videocon'
-    model_replacement: '$1'
-
-  #########
-  # Viewsonic
-  # @ref: http://viewsonic.com
-  #########
-  - regex: '; *((?:ViewPad|ViewPhone|VSD)[^;/]+) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Viewsonic'
-    model_replacement: '$1'
-  - regex: '; *(ViewSonic-)([^;/]+) Build/'
-    device_replacement: '$1$2'
-    brand_replacement: 'Viewsonic'
-    model_replacement: '$2'
-  - regex: '; *(GTablet.*) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Viewsonic'
-    model_replacement: '$1'
-
-  #########
-  # vivo
-  # @ref: http://vivo.cn/
-  #########
-  - regex: '; *([Vv]ivo)[ _]([^;/]+) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'vivo'
-    model_replacement: '$2'
-
-  #########
-  # Vodafone (Operator Branded Devices)
-  # @ref: ??
-  #########
-  - regex: '(Vodafone) (.*) Build/'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Walton
-  # @ref: http://www.waltonbd.com/
-  #########
-  - regex: '; *(?:Walton[ _\-])?(Primo[ _\-][^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: 'Walton $1'
-    brand_replacement: 'Walton'
-    model_replacement: '$1'
-
-  #########
-  # Wiko
-  # @ref: http://fr.wikomobile.com/collection.php?s=Smartphones
-  #########
-  - regex: '; *(?:WIKO[ \-])?(CINK\+?|BARRY|BLOOM|DARKFULL|DARKMOON|DARKNIGHT|DARKSIDE|FIZZ|HIGHWAY|IGGY|OZZY|RAINBOW|STAIRWAY|SUBLIM|WAX|CINK [^;/]+) Build/'
-    regex_flag: 'i'
-    device_replacement: 'Wiko $1'
-    brand_replacement: 'Wiko'
-    model_replacement: '$1'
-
-  #########
-  # WellcoM
-  # @ref: ??
-  #########
-  - regex: '; *WellcoM-([^;/]+) Build'
-    device_replacement: 'Wellcom $1'
-    brand_replacement: 'Wellcom'
-    model_replacement: '$1'
-
-  ##########
-  # WeTab
-  # @ref: http://wetab.mobi/
-  ##########
-  - regex: '(?:(WeTab)-Browser|; (wetab) Build)'
-    device_replacement: '$1'
-    brand_replacement: 'WeTab'
-    model_replacement: 'WeTab'
-
-  #########
-  # Wolfgang
-  # @ref: http://wolfgangmobile.com/
-  #########
-  - regex: '; *(AT-AS[^;/]+) Build'
-    device_replacement: 'Wolfgang $1'
-    brand_replacement: 'Wolfgang'
-    model_replacement: '$1'
-
-  #########
-  # Woxter
-  # @ref: http://www.woxter.es/es-es/categories/index
-  #########
-  - regex: '; *(?:Woxter|Wxt) ([^;/]+) Build'
-    device_replacement: 'Woxter $1'
-    brand_replacement: 'Woxter'
-    model_replacement: '$1'
-
-  #########
-  # Yarvik Zania
-  # @ref: http://yarvik.com
-  #########
-  - regex: '; *(?:Xenta |Luna )?(TAB[234][0-9]{2}|TAB0[78]-\d{3}|TAB0?9-\d{3}|TAB1[03]-\d{3}|SMP\d{2}-\d{3}) Build/'
-    device_replacement: 'Yarvik $1'
-    brand_replacement: 'Yarvik'
-    model_replacement: '$1'
-
-  #########
-  # Yifang
-  # @note: Needs to be at the very last as manufacturer builds for other brands.
-  # @ref: http://www.yifangdigital.com/
-  # @models: M1010, M1011, M1007, M1008, M1005, M899, M899LP, M909, M8000,
-  #   M8001, M8002, M8003, M849, M815, M816, M819, M805, M878, M780LPW,
-  #   M778, M7000, M7000AD, M7000NBD, M7001, M7002, M7002KBD, M777, M767,
-  #   M789, M799, M769, M757, M755, M753, M752, M739, M729, M723, M712, M727
-  #########
-  - regex: '; *([A-Z]{2,4})(M\d{3,}[A-Z]{2})([^;\)\/]*)(?: Build|[;\)])'
-    device_replacement: 'Yifang $1$2$3'
-    brand_replacement: 'Yifang'
-    model_replacement: '$2'
-
-  #########
-  # XiaoMi
-  # @ref: http://www.xiaomi.com/event/buyphone
-  #########
-  - regex: '; *((Mi|MI|HM|MI-ONE|Redmi)[ -](NOTE |Note )?[^;/]*) (Build|MIUI)/'
-    device_replacement: 'XiaoMi $1'
-    brand_replacement: 'XiaoMi'
-    model_replacement: '$1'
-
-  #########
-  # Xolo
-  # @ref: http://www.xolo.in/
-  #########
-  - regex: '; *XOLO[ _]([^;/]*tab.*) Build'
-    regex_flag: 'i'
-    device_replacement: 'Xolo $1'
-    brand_replacement: 'Xolo'
-    model_replacement: '$1'
-  - regex: '; *XOLO[ _]([^;/]+) Build'
-    regex_flag: 'i'
-    device_replacement: 'Xolo $1'
-    brand_replacement: 'Xolo'
-    model_replacement: '$1'
-  - regex: '; *(q\d0{2,3}[a-z]?) Build'
-    regex_flag: 'i'
-    device_replacement: 'Xolo $1'
-    brand_replacement: 'Xolo'
-    model_replacement: '$1'
-
-  #########
-  # Xoro
-  # @ref: http://www.xoro.de/produkte/
-  #########
-  - regex: '; *(PAD ?[79]\d+[^;/]*|TelePAD\d+[^;/]) Build'
-    device_replacement: 'Xoro $1'
-    brand_replacement: 'Xoro'
-    model_replacement: '$1'
-
-  #########
-  # Zopo
-  # @ref: http://www.zopomobiles.com/products.html
-  #########
-  - regex: '; *(?:(?:ZOPO|Zopo)[ _]([^;/]+)|(ZP ?(?:\d{2}[^;/]+|C2))|(C[2379])) Build'
-    device_replacement: '$1$2$3'
-    brand_replacement: 'Zopo'
-    model_replacement: '$1$2$3'
-
-  #########
-  # ZiiLabs
-  # @ref: http://www.ziilabs.com/products/platforms/androidreferencetablets.php
-  #########
-  - regex: '; *(ZiiLABS) (Zii[^;/]*) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'ZiiLabs'
-    model_replacement: '$2'
-  - regex: '; *(Zii)_([^;/]*) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'ZiiLabs'
-    model_replacement: '$2'
-
-  #########
-  # ZTE
-  # @ref: http://www.ztedevices.com/
-  #########
-  - regex: '; *(ARIZONA|(?:ATLAS|Atlas) W|D930|Grand (?:[SX][^;]*|Era|Memo[^;]*)|JOE|(?:Kis|KIS)\b[^;]*|Libra|Light [^;]*|N8[056][01]|N850L|N8000|N9[15]\d{2}|N9810|NX501|Optik|(?:Vip )Racer[^;]*|RacerII|RACERII|San Francisco[^;]*|V9[AC]|V55|V881|Z[679][0-9]{2}[A-z]?) Build'
-    device_replacement: '$1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-  - regex: '; *([A-Z]\d+)_USA_[^;]* Build'
-    device_replacement: '$1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-  - regex: '; *(SmartTab\d+)[^;]* Build'
-    device_replacement: '$1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-  - regex: '; *(?:Blade|BLADE|ZTE-BLADE)([^;/]*) Build'
-    device_replacement: 'ZTE Blade$1'
-    brand_replacement: 'ZTE'
-    model_replacement: 'Blade$1'
-  - regex: '; *(?:Skate|SKATE|ZTE-SKATE)([^;/]*) Build'
-    device_replacement: 'ZTE Skate$1'
-    brand_replacement: 'ZTE'
-    model_replacement: 'Skate$1'
-  - regex: '; *(Orange |Optimus )(Monte Carlo|San Francisco) Build'
-    device_replacement: '$1$2'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1$2'
-  - regex: '; *(?:ZXY-ZTE_|ZTE\-U |ZTE[\- _]|ZTE-C[_ ])([^;/]+) Build'
-    device_replacement: 'ZTE $1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-  # operator specific
-  - regex: '; (BASE) (lutea|Lutea 2|Tab[^;]*) Build'
-    device_replacement: '$1 $2'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1 $2'
-  - regex: '; (Avea inTouch 2|soft stone|tmn smart a7|Movistar[ _]Link) Build'
-    regex_flag: 'i'
-    device_replacement: '$1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-  - regex: '; *(vp9plus)\)'
-    device_replacement: '$1'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1'
-
-  ##########
-  # Zync
-  # @ref: http://www.zync.in/index.php/our-products/tablet-phablets
-  ##########
-  - regex: '; ?(Cloud[ _]Z5|z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900) Build/'
-    device_replacement: '$1'
-    brand_replacement: 'Zync'
-    model_replacement: '$1'
-
-  ##########
-  # Kindle
-  # @note: Needs to be after Sony Playstation Vita as this UA contains Silk/3.2
-  # @ref: https://developer.amazon.com/sdk/fire/specifications.html
-  # @ref: http://amazonsilk.wordpress.com/useful-bits/silk-user-agent/
-  ##########
-  - regex: '; ?(KFOT|Kindle Fire) Build\b'
-    device_replacement: 'Kindle Fire'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire'
-  - regex: '; ?(KFOTE|Amazon Kindle Fire2) Build\b'
-    device_replacement: 'Kindle Fire 2'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire 2'
-  - regex: '; ?(KFTT) Build\b'
-    device_replacement: 'Kindle Fire HD'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HD 7"'
-  - regex: '; ?(KFJWI) Build\b'
-    device_replacement: 'Kindle Fire HD 8.9" WiFi'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HD 8.9" WiFi'
-  - regex: '; ?(KFJWA) Build\b'
-    device_replacement: 'Kindle Fire HD 8.9" 4G'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HD 8.9" 4G'
-  - regex: '; ?(KFSOWI) Build\b'
-    device_replacement: 'Kindle Fire HD 7" WiFi'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HD 7" WiFi'
-  - regex: '; ?(KFTHWI) Build\b'
-    device_replacement: 'Kindle Fire HDX 7" WiFi'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HDX 7" WiFi'
-  - regex: '; ?(KFTHWA) Build\b'
-    device_replacement: 'Kindle Fire HDX 7" 4G'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HDX 7" 4G'
-  - regex: '; ?(KFAPWI) Build\b'
-    device_replacement: 'Kindle Fire HDX 8.9" WiFi'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HDX 8.9" WiFi'
-  - regex: '; ?(KFAPWA) Build\b'
-    device_replacement: 'Kindle Fire HDX 8.9" 4G'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire HDX 8.9" 4G'
-  - regex: '; ?Amazon ([^;/]+) Build\b'
-    device_replacement: '$1'
-    brand_replacement: 'Amazon'
-    model_replacement: '$1'
-  - regex: '; ?(Kindle) Build\b'
-    device_replacement: 'Kindle'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle'
-  - regex: '; ?(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))? Build\b'
-    device_replacement: 'Kindle Fire'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle Fire$2'
-  - regex: ' (Kindle)/(\d+\.\d+)'
-    device_replacement: 'Kindle'
-    brand_replacement: 'Amazon'
-    model_replacement: '$1 $2'
-  - regex: ' (Silk|Kindle)/(\d+)\.'
-    device_replacement: 'Kindle'
-    brand_replacement: 'Amazon'
-    model_replacement: 'Kindle'
-
-  #########
-  # Devices from chinese manufacturer(s)
-  # @note: identified by x-wap-profile http://218.249.47.94/Xianghe/.*
-  #########
-  - regex: '(sprd)\-([^/]+)/'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  # @ref: http://eshinechina.en.alibaba.com/
-  - regex: '; *(H\d{2}00\+?) Build'
-    device_replacement: '$1'
-    brand_replacement: 'Hero'
-    model_replacement: '$1'
-  - regex: '; *(iphone|iPhone5) Build/'
-    device_replacement: 'Xianghe $1'
-    brand_replacement: 'Xianghe'
-    model_replacement: '$1'
-  - regex: '; *(e\d{4}[a-z]?_?v\d+|v89_[^;/]+)[^;/]+ Build/'
-    device_replacement: 'Xianghe $1'
-    brand_replacement: 'Xianghe'
-    model_replacement: '$1'
-
-  #########
-  # Cellular
-  # @ref:
-  # @note: Operator branded devices
-  #########
-  - regex: '\bUSCC[_\-]?([^ ;/\)]+)'
-    device_replacement: '$1'
-    brand_replacement: 'Cellular'
-    model_replacement: '$1'
-
-  ######################################################################
-  # Windows Phone Parsers
-  ######################################################################
-
-  #########
-  # Alcatel Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:ALCATEL)[^;]*; *([^;,\)]+)'
-    device_replacement: 'Alcatel $1'
-    brand_replacement: 'Alcatel'
-    model_replacement: '$1'
-
-  #########
-  # Asus Windows Phones
-  #########
-  #~ - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?|WpsLondonTest; ?)?(?:ASUS|Asus)[^;]*; *([^;,\)]+)'
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?|WpsLondonTest; ?)?(?:ASUS|Asus)[^;]*; *([^;,\)]+)'
-    device_replacement: 'Asus $1'
-    brand_replacement: 'Asus'
-    model_replacement: '$1'
-
-  #########
-  # Dell Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:DELL|Dell)[^;]*; *([^;,\)]+)'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: '$1'
-
-  #########
-  # HTC Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?|WpsLondonTest; ?)?(?:HTC|Htc|HTC_blocked[^;]*)[^;]*; *(?:HTC)?([^;,\)]+)'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-
-  #########
-  # Huawei Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:HUAWEI)[^;]*; *(?:HUAWEI )?([^;,\)]+)'
-    device_replacement: 'Huawei $1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-
-  #########
-  # LG Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:LG|Lg)[^;]*; *(?:LG[ \-])?([^;,\)]+)'
-    device_replacement: 'LG $1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-
-  #########
-  # Noka Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:rv:11; )?(?:NOKIA|Nokia)[^;]*; *(?:NOKIA ?|Nokia ?|LUMIA ?|[Ll]umia ?)*(\d{3,}[^;\)]*)'
-    device_replacement: 'Lumia $1'
-    brand_replacement: 'Nokia'
-    model_replacement: 'Lumia $1'
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:NOKIA|Nokia)[^;]*; *(RM-\d{3,})'
-    device_replacement: 'Nokia $1'
-    brand_replacement: 'Nokia'
-    model_replacement: '$1'
-  - regex: '(?:Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)]|WPDesktop;) ?(?:ARM; ?Touch; ?|Touch; ?)?(?:NOKIA|Nokia)[^;]*; *(?:NOKIA ?|Nokia ?|LUMIA ?|[Ll]umia ?)*([^;\)]+)'
-    device_replacement: 'Nokia $1'
-    brand_replacement: 'Nokia'
-    model_replacement: '$1'
-
-  #########
-  # Microsoft Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?)?(?:Microsoft(?: Corporation)?)[^;]*; *([^;,\)]+)'
-    device_replacement: 'Microsoft $1'
-    brand_replacement: 'Microsoft'
-    model_replacement: '$1'
-
-  #########
-  # Samsung Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?|WpsLondonTest; ?)?(?:SAMSUNG)[^;]*; *(?:SAMSUNG )?([^;,\.\)]+)'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-
-  #########
-  # Toshiba Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?|WpsLondonTest; ?)?(?:TOSHIBA|FujitsuToshibaMobileCommun)[^;]*; *([^;,\)]+)'
-    device_replacement: 'Toshiba $1'
-    brand_replacement: 'Toshiba'
-    model_replacement: '$1'
-
-  #########
-  # Generic Windows Phones
-  #########
-  - regex: 'Windows Phone [^;]+; .*?IEMobile/[^;\)]+[;\)] ?(?:ARM; ?Touch; ?|Touch; ?|WpsLondonTest; ?)?([^;]+); *([^;,\)]+)'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  ######################################################################
-  # Other Devices Parser
-  ######################################################################
-
-  #########
-  # Samsung Bada Phones
-  #########
-  - regex: '(?:^|; )SAMSUNG\-([A-Za-z0-9\-]+).* Bada/'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-
-  #########
-  # Firefox OS
-  #########
-  - regex: '\(Mobile; ALCATEL ?(One|ONE) ?(Touch|TOUCH) ?([^;/]+)(?:/[^;]+)?; rv:[^\)]+\) Gecko/[^\/]+ Firefox/'
-    device_replacement: 'Alcatel $1 $2 $3'
-    brand_replacement: 'Alcatel'
-    model_replacement: 'One Touch $3'
-  - regex: '\(Mobile; (?:ZTE([^;]+)|(OpenC)); rv:[^\)]+\) Gecko/[^\/]+ Firefox/'
-    device_replacement: 'ZTE $1$2'
-    brand_replacement: 'ZTE'
-    model_replacement: '$1$2'
-
-  ##########
-  # NOKIA
-  # @note: NokiaN8-00 comes before iphone. Sometimes spoofs iphone
-  ##########
-  - regex: 'Nokia(N[0-9]+)([A-z_\-][A-z0-9_\-]*)'
-    device_replacement: 'Nokia $1'
-    brand_replacement: 'Nokia'
-    model_replacement: '$1$2'
-  - regex: '(?:NOKIA|Nokia)(?:\-| *)(?:([A-Za-z0-9]+)\-[0-9a-f]{32}|([A-Za-z0-9\-]+)(?:UCBrowser)|([A-Za-z0-9\-]+))'
-    device_replacement: 'Nokia $1$2$3'
-    brand_replacement: 'Nokia'
-    model_replacement: '$1$2$3'
-  - regex: 'Lumia ([A-Za-z0-9\-]+)'
-    device_replacement: 'Lumia $1'
-    brand_replacement: 'Nokia'
-    model_replacement: 'Lumia $1'
-  # UCWEB Browser on Symbian
-  - regex: '\(Symbian; U; S60 V5; [A-z]{2}\-[A-z]{2}; (SonyEricsson|Samsung|Nokia|LG)([^;/]+)\)'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  # Nokia Symbian
-  - regex: '\(Symbian(?:/3)?; U; ([^;]+);'
-    device_replacement: 'Nokia $1'
-    brand_replacement: 'Nokia'
-    model_replacement: '$1'
-
-  ##########
-  # BlackBerry
-  # @ref: http://www.useragentstring.com/pages/BlackBerry/
-  ##########
-  - regex: 'BB10; ([A-Za-z0-9\- ]+)\)'
-    device_replacement: 'BlackBerry $1'
-    brand_replacement: 'BlackBerry'
-    model_replacement: '$1'
-  - regex: 'Play[Bb]ook.+RIM Tablet OS'
-    device_replacement: 'BlackBerry Playbook'
-    brand_replacement: 'BlackBerry'
-    model_replacement: 'Playbook'
-  - regex: 'Black[Bb]erry ([0-9]+);'
-    device_replacement: 'BlackBerry $1'
-    brand_replacement: 'BlackBerry'
-    model_replacement: '$1'
-  - regex: 'Black[Bb]erry([0-9]+)'
-    device_replacement: 'BlackBerry $1'
-    brand_replacement: 'BlackBerry'
-    model_replacement: '$1'
-  - regex: 'Black[Bb]erry;'
-    device_replacement: 'BlackBerry'
-    brand_replacement: 'BlackBerry'
-
-  ##########
-  # PALM / HP
-  # @note: some palm devices must come before iphone. sometimes spoofs iphone in ua
-  ##########
-  - regex: '(Pre|Pixi)/\d+\.\d+'
-    device_replacement: 'Palm $1'
-    brand_replacement: 'Palm'
-    model_replacement: '$1'
-  - regex: 'Palm([0-9]+)'
-    device_replacement: 'Palm $1'
-    brand_replacement: 'Palm'
-    model_replacement: '$1'
-  - regex: 'Treo([A-Za-z0-9]+)'
-    device_replacement: 'Palm Treo $1'
-    brand_replacement: 'Palm'
-    model_replacement: 'Treo $1'
-  - regex: 'webOS.*(P160U(?:NA)?)/(\d+).(\d+)'
-    device_replacement: 'HP Veer'
-    brand_replacement: 'HP'
-    model_replacement: 'Veer'
-  - regex: '(Touch[Pp]ad)/\d+\.\d+'
-    device_replacement: 'HP TouchPad'
-    brand_replacement: 'HP'
-    model_replacement: 'TouchPad'
-  - regex: 'HPiPAQ([A-Za-z0-9]+)/\d+.\d+'
-    device_replacement: 'HP iPAQ $1'
-    brand_replacement: 'HP'
-    model_replacement: 'iPAQ $1'
-  - regex: 'PDA; (PalmOS)/sony/model ([a-z]+)/Revision'
-    device_replacement: '$1'
-    brand_replacement: 'Sony'
-    model_replacement: '$1 $2'
-
-  ##########
-  # AppleTV
-  # No built in browser that I can tell
-  # Stack Overflow indicated iTunes-AppleTV/4.1 as a known UA for app available and I'm seeing it in live traffic
-  ##########
-  - regex: '(Apple\s?TV)'
-    device_replacement: 'AppleTV'
-    brand_replacement: 'Apple'
-    model_replacement: 'AppleTV'
-
-  #########
-  # Tesla Model S
-  #########
-  - regex: '(QtCarBrowser)'
-    device_replacement: 'Tesla Model S'
-    brand_replacement: 'Tesla'
-    model_replacement: 'Model S'
-
-  ##########
-  # iSTUFF
-  # @note: complete but probably catches spoofs
-  #   ipad and ipod must be parsed before iphone
-  #   cannot determine specific device type from ua string. (3g, 3gs, 4, etc)
-  ##########
-  # @note: on some ua the device can be identified e.g. iPhone5,1
-  - regex: '(iPhone|iPad|iPod)(\d+,\d+)'
-    device_replacement: '$1'
-    brand_replacement: 'Apple'
-    model_replacement: '$1$2'
-  # @note: iPad needs to be before iPhone
-  - regex: '(iPad)(?:;| Simulator;)'
-    device_replacement: '$1'
-    brand_replacement: 'Apple'
-    model_replacement: '$1'
-  - regex: '(iPod)(?:;| touch;| Simulator;)'
-    device_replacement: '$1'
-    brand_replacement: 'Apple'
-    model_replacement: '$1'
-  - regex: '(iPhone)(?:;| Simulator;)'
-    device_replacement: '$1'
-    brand_replacement: 'Apple'
-    model_replacement: '$1'
-  - regex: '(Apple Watch)(?:;| Simulator;)'
-    device_replacement: '$1'
-    brand_replacement: 'Apple'
-    model_replacement: '$1'
-  - regex: '(HomePod)(?:;| Simulator;)'
-    device_replacement: '$1'
-    brand_replacement: 'Apple'
-    model_replacement: '$1'
-  - regex: 'iPhone'
-    device_replacement: 'iPhone'
-    brand_replacement: 'Apple'
-    model_replacement: 'iPhone'
-  # @note: desktop applications show device info
-  - regex: 'CFNetwork/.* Darwin/\d.*\(((?:Mac|iMac|PowerMac|PowerBook)[^\d]*)(\d+)(?:,|%2C)(\d+)'
-    device_replacement: '$1$2,$3'
-    brand_replacement: 'Apple'
-    model_replacement: '$1$2,$3'
-  # @note: newer desktop applications don't show device info
-  # This is here so as to not have them recorded as iOS-Device
-  - regex: 'CFNetwork/.* Darwin/\d+\.\d+\.\d+ \(x86_64\)'
-    device_replacement: 'Mac'
-    brand_replacement: 'Apple'
-    model_replacement: 'Mac'
-  # @note: iOS applications do not show device info
-  - regex: 'CFNetwork/.* Darwin/\d'
-    device_replacement: 'iOS-Device'
-    brand_replacement: 'Apple'
-    model_replacement: 'iOS-Device'
-
-  ##########################
-  # Outlook on iOS >= 2.62.0
-  ##########################
-  - regex: 'Outlook-(iOS)/\d+\.\d+\.prod\.iphone'
-    brand_replacement: 'Apple'
-    device_replacement: 'iPhone'
-    model_replacement: 'iPhone'
-
-  ##########
-  # Acer
-  ##########
-  - regex: 'acer_([A-Za-z0-9]+)_'
-    device_replacement: 'Acer $1'
-    brand_replacement: 'Acer'
-    model_replacement: '$1'
-
-  ##########
-  # Alcatel
-  ##########
-  - regex: '(?:ALCATEL|Alcatel)-([A-Za-z0-9\-]+)'
-    device_replacement: 'Alcatel $1'
-    brand_replacement: 'Alcatel'
-    model_replacement: '$1'
-
-  ##########
-  # Amoi
-  ##########
-  - regex: '(?:Amoi|AMOI)\-([A-Za-z0-9]+)'
-    device_replacement: 'Amoi $1'
-    brand_replacement: 'Amoi'
-    model_replacement: '$1'
-
-  ##########
-  # Asus
-  ##########
-  - regex: '(?:; |\/|^)((?:Transformer (?:Pad|Prime) |Transformer |PadFone[ _]?)[A-Za-z0-9]*)'
-    device_replacement: 'Asus $1'
-    brand_replacement: 'Asus'
-    model_replacement: '$1'
-  - regex: '(?:asus.*?ASUS|Asus|ASUS|asus)[\- ;]*((?:Transformer (?:Pad|Prime) |Transformer |Padfone |Nexus[ _])?[A-Za-z0-9]+)'
-    device_replacement: 'Asus $1'
-    brand_replacement: 'Asus'
-    model_replacement: '$1'
-
-
-  ##########
-  # Bird
-  ##########
-  - regex: '\bBIRD[ \-\.]([A-Za-z0-9]+)'
-    device_replacement: 'Bird $1'
-    brand_replacement: 'Bird'
-    model_replacement: '$1'
-
-  ##########
-  # Dell
-  ##########
-  - regex: '\bDell ([A-Za-z0-9]+)'
-    device_replacement: 'Dell $1'
-    brand_replacement: 'Dell'
-    model_replacement: '$1'
-
-  ##########
-  # DoCoMo
-  ##########
-  - regex: 'DoCoMo/2\.0 ([A-Za-z0-9]+)'
-    device_replacement: 'DoCoMo $1'
-    brand_replacement: 'DoCoMo'
-    model_replacement: '$1'
-  - regex: '([A-Za-z0-9]+)_W;FOMA'
-    device_replacement: 'DoCoMo $1'
-    brand_replacement: 'DoCoMo'
-    model_replacement: '$1'
-  - regex: '([A-Za-z0-9]+);FOMA'
-    device_replacement: 'DoCoMo $1'
-    brand_replacement: 'DoCoMo'
-    model_replacement: '$1'
-
-  ##########
-  # htc
-  ##########
-  - regex: '\b(?:HTC/|HTC/[a-z0-9]+/)?HTC[ _\-;]? *(.*?)(?:-?Mozilla|fingerPrint|[;/\(\)]|$)'
-    device_replacement: 'HTC $1'
-    brand_replacement: 'HTC'
-    model_replacement: '$1'
-
-  ##########
-  # Huawei
-  ##########
-  - regex: 'Huawei([A-Za-z0-9]+)'
-    device_replacement: 'Huawei $1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-  - regex: 'HUAWEI-([A-Za-z0-9]+)'
-    device_replacement: 'Huawei $1'
-    brand_replacement: 'Huawei'
-    model_replacement: '$1'
-  - regex: 'vodafone([A-Za-z0-9]+)'
-    device_replacement: 'Huawei Vodafone $1'
-    brand_replacement: 'Huawei'
-    model_replacement: 'Vodafone $1'
-
-  ##########
-  # i-mate
-  ##########
-  - regex: 'i\-mate ([A-Za-z0-9]+)'
-    device_replacement: 'i-mate $1'
-    brand_replacement: 'i-mate'
-    model_replacement: '$1'
-
-  ##########
-  # kyocera
-  ##########
-  - regex: 'Kyocera\-([A-Za-z0-9]+)'
-    device_replacement: 'Kyocera $1'
-    brand_replacement: 'Kyocera'
-    model_replacement: '$1'
-  - regex: 'KWC\-([A-Za-z0-9]+)'
-    device_replacement: 'Kyocera $1'
-    brand_replacement: 'Kyocera'
-    model_replacement: '$1'
-
-  ##########
-  # lenovo
-  ##########
-  - regex: 'Lenovo[_\-]([A-Za-z0-9]+)'
-    device_replacement: 'Lenovo $1'
-    brand_replacement: 'Lenovo'
-    model_replacement: '$1'
-
-  ##########
-  # HbbTV (European and Australian standard)
-  # written before the LG regexes, as LG is making HbbTV too
-  ##########
-  - regex: '(HbbTV)/[0-9]+\.[0-9]+\.[0-9]+ \([^;]*; *(LG)E *; *([^;]*) *;[^;]*;[^;]*;\)'
-    device_replacement: '$1'
-    brand_replacement: '$2'
-    model_replacement: '$3'
-  - regex: '(HbbTV)/1\.1\.1.*CE-HTML/1\.\d;(Vendor/)*(THOM[^;]*?)[;\s](?:.*SW-Version/.*)*(LF[^;]+);?'
-    device_replacement: '$1'
-    brand_replacement: 'Thomson'
-    model_replacement: '$4'
-  - regex: '(HbbTV)(?:/1\.1\.1)?(?: ?\(;;;;;\))?; *CE-HTML(?:/1\.\d)?; *([^ ]+) ([^;]+);'
-    device_replacement: '$1'
-    brand_replacement: '$2'
-    model_replacement: '$3'
-  - regex: '(HbbTV)/1\.1\.1 \(;;;;;\) Maple_2011'
-    device_replacement: '$1'
-    brand_replacement: 'Samsung'
-  - regex: '(HbbTV)/[0-9]+\.[0-9]+\.[0-9]+ \([^;]*; *(?:CUS:([^;]*)|([^;]+)) *; *([^;]*) *;.*;'
-    device_replacement: '$1'
-    brand_replacement: '$2$3'
-    model_replacement: '$4'
-  - regex: '(HbbTV)/[0-9]+\.[0-9]+\.[0-9]+'
-    device_replacement: '$1'
-
-  ##########
-  # LGE NetCast TV
-  ##########
-  - regex: 'LGE; (?:Media\/)?([^;]*);[^;]*;[^;]*;?\); "?LG NetCast(\.TV|\.Media)?-\d+'
-    device_replacement: 'NetCast$2'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-
-  ##########
-  # InettvBrowser
-  ##########
-  - regex: 'InettvBrowser/[0-9]+\.[0-9A-Z]+ \([^;]*;(Sony)([^;]*);[^;]*;[^\)]*\)'
-    device_replacement: 'Inettv'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  - regex: 'InettvBrowser/[0-9]+\.[0-9A-Z]+ \([^;]*;([^;]*);[^;]*;[^\)]*\)'
-    device_replacement: 'Inettv'
-    brand_replacement: 'Generic_Inettv'
-    model_replacement: '$1'
-  - regex: '(?:InettvBrowser|TSBNetTV|NETTV|HBBTV)'
-    device_replacement: 'Inettv'
-    brand_replacement: 'Generic_Inettv'
-
-  ##########
-  # lg
-  ##########
-  # LG Symbian Phones
-  - regex: 'Series60/\d\.\d (LG)[\-]?([A-Za-z0-9 \-]+)'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-  # other LG phones
-  - regex: '\b(?:LGE[ \-]LG\-(?:AX)?|LGE |LGE?-LG|LGE?[ \-]|LG[ /\-]|lg[\-])([A-Za-z0-9]+)\b'
-    device_replacement: 'LG $1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-  - regex: '(?:^LG[\-]?|^LGE[\-/]?)([A-Za-z]+[0-9]+[A-Za-z]*)'
-    device_replacement: 'LG $1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-  - regex: '^LG([0-9]+[A-Za-z]*)'
-    device_replacement: 'LG $1'
-    brand_replacement: 'LG'
-    model_replacement: '$1'
-
-  ##########
-  # microsoft
-  ##########
-  - regex: '(KIN\.[^ ]+) (\d+)\.(\d+)'
-    device_replacement: 'Microsoft $1'
-    brand_replacement: 'Microsoft'
-    model_replacement: '$1'
-  - regex: '(?:MSIE|XBMC).*\b(Xbox)\b'
-    device_replacement: '$1'
-    brand_replacement: 'Microsoft'
-    model_replacement: '$1'
-  - regex: '; ARM; Trident/6\.0; Touch[\);]'
-    device_replacement: 'Microsoft Surface RT'
-    brand_replacement: 'Microsoft'
-    model_replacement: 'Surface RT'
-
-  ##########
-  # motorola
-  ##########
-  - regex: 'Motorola\-([A-Za-z0-9]+)'
-    device_replacement: 'Motorola $1'
-    brand_replacement: 'Motorola'
-    model_replacement: '$1'
-  - regex: 'MOTO\-([A-Za-z0-9]+)'
-    device_replacement: 'Motorola $1'
-    brand_replacement: 'Motorola'
-    model_replacement: '$1'
-  - regex: 'MOT\-([A-z0-9][A-z0-9\-]*)'
-    device_replacement: 'Motorola $1'
-    brand_replacement: 'Motorola'
-    model_replacement: '$1'
-
-  ##########
-  # nintendo
-  ##########
-  - regex: 'Nintendo WiiU'
-    device_replacement: 'Nintendo Wii U'
-    brand_replacement: 'Nintendo'
-    model_replacement: 'Wii U'
-  - regex: 'Nintendo (DS|3DS|DSi|Wii);'
-    device_replacement: 'Nintendo $1'
-    brand_replacement: 'Nintendo'
-    model_replacement: '$1'
-
-  ##########
-  # pantech
-  ##########
-  - regex: '(?:Pantech|PANTECH)[ _-]?([A-Za-z0-9\-]+)'
-    device_replacement: 'Pantech $1'
-    brand_replacement: 'Pantech'
-    model_replacement: '$1'
-
-  ##########
-  # philips
-  ##########
-  - regex: 'Philips([A-Za-z0-9]+)'
-    device_replacement: 'Philips $1'
-    brand_replacement: 'Philips'
-    model_replacement: '$1'
-  - regex: 'Philips ([A-Za-z0-9]+)'
-    device_replacement: 'Philips $1'
-    brand_replacement: 'Philips'
-    model_replacement: '$1'
-
-  ##########
-  # Samsung
-  ##########
-  # Samsung Smart-TV
-  - regex: '(SMART-TV); .* Tizen '
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  # Samsung Symbian Devices
-  - regex: 'SymbianOS/9\.\d.* Samsung[/\-]([A-Za-z0-9 \-]+)'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  - regex: '(Samsung)(SGH)(i[0-9]+)'
-    device_replacement: '$1 $2$3'
-    brand_replacement: '$1'
-    model_replacement: '$2-$3'
-  - regex: 'SAMSUNG-ANDROID-MMS/([^;/]+)'
-    device_replacement: '$1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-  # Other Samsung
-  #- regex: 'SAMSUNG(?:; |-)([A-Za-z0-9\-]+)'
-  - regex: 'SAMSUNG(?:; |[ -/])([A-Za-z0-9\-]+)'
-    regex_flag: 'i'
-    device_replacement: 'Samsung $1'
-    brand_replacement: 'Samsung'
-    model_replacement: '$1'
-
-  ##########
-  # Sega
-  ##########
-  - regex: '(Dreamcast)'
-    device_replacement: 'Sega $1'
-    brand_replacement: 'Sega'
-    model_replacement: '$1'
-
-  ##########
-  # Siemens mobile
-  ##########
-  - regex: '^SIE-([A-Za-z0-9]+)'
-    device_replacement: 'Siemens $1'
-    brand_replacement: 'Siemens'
-    model_replacement: '$1'
-
-  ##########
-  # Softbank
-  ##########
-  - regex: 'Softbank/[12]\.0/([A-Za-z0-9]+)'
-    device_replacement: 'Softbank $1'
-    brand_replacement: 'Softbank'
-    model_replacement: '$1'
-
-  ##########
-  # SonyEricsson
-  ##########
-  - regex: 'SonyEricsson ?([A-Za-z0-9\-]+)'
-    device_replacement: 'Ericsson $1'
-    brand_replacement: 'SonyEricsson'
-    model_replacement: '$1'
-
-  ##########
-  # Sony
-  ##########
-  - regex: 'Android [^;]+; ([^ ]+) (Sony)/'
-    device_replacement: '$2 $1'
-    brand_replacement: '$2'
-    model_replacement: '$1'
-  - regex: '(Sony)(?:BDP\/|\/)?([^ /;\)]+)[ /;\)]'
-    device_replacement: '$1 $2'
-    brand_replacement: '$1'
-    model_replacement: '$2'
-
-  #########
-  # Puffin Browser Device detect
-  # A=Android, I=iOS, P=Phone, T=Tablet
-  # AT=Android+Tablet
-  #########
-  - regex: 'Puffin/[\d\.]+IT'
-    device_replacement: 'iPad'
-    brand_replacement: 'Apple'
-    model_replacement: 'iPad'
-  - regex: 'Puffin/[\d\.]+IP'
-    device_replacement: 'iPhone'
-    brand_replacement: 'Apple'
-    model_replacement: 'iPhone'
-  - regex: 'Puffin/[\d\.]+AT'
-    device_replacement: 'Generic Tablet'
-    brand_replacement: 'Generic'
-    model_replacement: 'Tablet'
-  - regex: 'Puffin/[\d\.]+AP'
-    device_replacement: 'Generic Smartphone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Smartphone'
-
-  #########
-  # Android General Device Matching (far from perfect)
-  #########
-  - regex: 'Android[\- ][\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{0,2}; WOWMobile (.+) Build[/ ]'
-    brand_replacement: 'Generic_Android'
-    model_replacement: '$1'
-  - regex: 'Android[\- ][\d]+\.[\d]+\-update1; [A-Za-z]{2}\-[A-Za-z]{0,2} *; *(.+?) Build[/ ]'
-    brand_replacement: 'Generic_Android'
-    model_replacement: '$1'
-  - regex: 'Android[\- ][\d]+(?:\.[\d]+){1,2}; *[A-Za-z]{2}[_\-][A-Za-z]{0,2}\-? *; *(.+?) Build[/ ]'
-    brand_replacement: 'Generic_Android'
-    model_replacement: '$1'
-  - regex: 'Android[\- ][\d]+(?:\.[\d]+){1,2}; *[A-Za-z]{0,2}\- *; *(.+?) Build[/ ]'
-    brand_replacement: 'Generic_Android'
-    model_replacement: '$1'
-  # No build info at all - "Build" follows locale immediately
-  - regex: 'Android[\- ][\d]+(?:\.[\d]+){1,2}; *[a-z]{0,2}[_\-]?[A-Za-z]{0,2};? Build[/ ]'
-    device_replacement: 'Generic Smartphone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Smartphone'
-  - regex: 'Android[\- ][\d]+(?:\.[\d]+){1,2}; *\-?[A-Za-z]{2}; *(.+?) Build[/ ]'
-    brand_replacement: 'Generic_Android'
-    model_replacement: '$1'
-  - regex: 'Android[\- ][\d]+(?:\.[\d]+){1,2}(?:;.*)?; *(.+?) Build[/ ]'
-    brand_replacement: 'Generic_Android'
-    model_replacement: '$1'
-
-  ##########
-  # Google TV
-  ##########
-  - regex: '(GoogleTV)'
-    brand_replacement: 'Generic_Inettv'
-    model_replacement: '$1'
-
-  ##########
-  # WebTV
-  ##########
-  - regex: '(WebTV)/\d+.\d+'
-    brand_replacement: 'Generic_Inettv'
-    model_replacement: '$1'
-  # Roku Digital-Video-Players https://www.roku.com/
-  - regex: '^(Roku)/DVP-\d+\.\d+'
-    brand_replacement: 'Generic_Inettv'
-    model_replacement: '$1'
-
-  ##########
-  # Generic Tablet
-  ##########
-  - regex: '(Android 3\.\d|Opera Tablet|Tablet; .+Firefox/|Android.*(?:Tab|Pad))'
-    regex_flag: 'i'
-    device_replacement: 'Generic Tablet'
-    brand_replacement: 'Generic'
-    model_replacement: 'Tablet'
-
-  ##########
-  # Generic Smart Phone
-  ##########
-  - regex: '(Symbian|\bS60(Version|V\d)|\bS60\b|\((Series 60|Windows Mobile|Palm OS|Bada); Opera Mini|Windows CE|Opera Mobi|BREW|Brew|Mobile; .+Firefox/|iPhone OS|Android|MobileSafari|Windows *Phone|\(webOS/|PalmOS)'
-    device_replacement: 'Generic Smartphone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Smartphone'
-  - regex: '(hiptop|avantgo|plucker|xiino|blazer|elaine)'
-    regex_flag: 'i'
-    device_replacement: 'Generic Smartphone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Smartphone'
-
-  ##########
-  # Spiders (this is hack...)
-  ##########
-  - regex: '(bot|BUbiNG|zao|borg|DBot|oegp|silk|Xenu|zeal|^NING|CCBot|crawl|htdig|lycos|slurp|teoma|voila|yahoo|Sogou|CiBra|Nutch|^Java/|^JNLP/|Daumoa|Daum|Genieo|ichiro|larbin|pompos|Scrapy|snappy|speedy|spider|msnbot|msrbot|vortex|^vortex|crawler|favicon|indexer|Riddler|scooter|scraper|scrubby|WhatWeb|WinHTTP|bingbot|BingPreview|openbot|gigabot|furlbot|polybot|seekbot|^voyager|archiver|Icarus6j|mogimogi|Netvibes|blitzbot|altavista|charlotte|findlinks|Retreiver|TLSProber|WordPress|SeznamBot|ProoXiBot|wsr\-agent|Squrl Java|EtaoSpider|PaperLiBot|SputnikBot|A6\-Indexer|netresearch|searchsight|baiduspider|YisouSpider|ICC\-Crawler|http%20client|Python-urllib|dataparksearch|converacrawler|Screaming Frog|AppEngine-Google|YahooCacheSystem|fast\-webcrawler|Sogou Pic Spider|semanticdiscovery|Innovazion Crawler|facebookexternalhit|Google.*/\+/web/snippet|Google-HTTP-Java-Client|BlogBridge|IlTrovatore-Setaccio|InternetArchive|GomezAgent|WebThumbnail|heritrix|NewsGator|PagePeeker|Reaper|ZooShot|holmes|NL-Crawler|Pingdom|StatusCake|WhatsApp|masscan|Google Web Preview|Qwantify|Yeti)'
-    regex_flag: 'i'
-    device_replacement: 'Spider'
-    brand_replacement: 'Spider'
-    model_replacement: 'Desktop'
-
-  ##########
-  # Generic Feature Phone
-  # take care to do case insensitive matching
-  ##########
-  - regex: '^(1207|3gso|4thp|501i|502i|503i|504i|505i|506i|6310|6590|770s|802s|a wa|acer|acs\-|airn|alav|asus|attw|au\-m|aur |aus |abac|acoo|aiko|alco|alca|amoi|anex|anny|anyw|aptu|arch|argo|bmobile|bell|bird|bw\-n|bw\-u|beck|benq|bilb|blac|c55/|cdm\-|chtm|capi|comp|cond|dall|dbte|dc\-s|dica|ds\-d|ds12|dait|devi|dmob|doco|dopo|dorado|el(?:38|39|48|49|50|55|58|68)|el[3456]\d{2}dual|erk0|esl8|ex300|ez40|ez60|ez70|ezos|ezze|elai|emul|eric|ezwa|fake|fly\-|fly_|g\-mo|g1 u|g560|gf\-5|grun|gene|go.w|good|grad|hcit|hd\-m|hd\-p|hd\-t|hei\-|hp i|hpip|hs\-c|htc |htc\-|htca|htcg)'
-    regex_flag: 'i'
-    device_replacement: 'Generic Feature Phone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Feature Phone'
-  - regex: '^(htcp|htcs|htct|htc_|haie|hita|huaw|hutc|i\-20|i\-go|i\-ma|i\-mobile|i230|iac|iac\-|iac/|ig01|im1k|inno|iris|jata|kddi|kgt|kgt/|kpt |kwc\-|klon|lexi|lg g|lg\-a|lg\-b|lg\-c|lg\-d|lg\-f|lg\-g|lg\-k|lg\-l|lg\-m|lg\-o|lg\-p|lg\-s|lg\-t|lg\-u|lg\-w|lg/k|lg/l|lg/u|lg50|lg54|lge\-|lge/|leno|m1\-w|m3ga|m50/|maui|mc01|mc21|mcca|medi|meri|mio8|mioa|mo01|mo02|mode|modo|mot |mot\-|mt50|mtp1|mtv |mate|maxo|merc|mits|mobi|motv|mozz|n100|n101|n102|n202|n203|n300|n302|n500|n502|n505|n700|n701|n710|nec\-|nem\-|newg|neon)'
-    regex_flag: 'i'
-    device_replacement: 'Generic Feature Phone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Feature Phone'
-  - regex: '^(netf|noki|nzph|o2 x|o2\-x|opwv|owg1|opti|oran|ot\-s|p800|pand|pg\-1|pg\-2|pg\-3|pg\-6|pg\-8|pg\-c|pg13|phil|pn\-2|pt\-g|palm|pana|pire|pock|pose|psio|qa\-a|qc\-2|qc\-3|qc\-5|qc\-7|qc07|qc12|qc21|qc32|qc60|qci\-|qwap|qtek|r380|r600|raks|rim9|rove|s55/|sage|sams|sc01|sch\-|scp\-|sdk/|se47|sec\-|sec0|sec1|semc|sgh\-|shar|sie\-|sk\-0|sl45|slid|smb3|smt5|sp01|sph\-|spv |spv\-|sy01|samm|sany|sava|scoo|send|siem|smar|smit|soft|sony|t\-mo|t218|t250|t600|t610|t618|tcl\-|tdg\-|telm|tim\-|ts70|tsm\-|tsm3|tsm5|tx\-9|tagt)'
-    regex_flag: 'i'
-    device_replacement: 'Generic Feature Phone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Feature Phone'
-  - regex: '^(talk|teli|topl|tosh|up.b|upg1|utst|v400|v750|veri|vk\-v|vk40|vk50|vk52|vk53|vm40|vx98|virg|vertu|vite|voda|vulc|w3c |w3c\-|wapj|wapp|wapu|wapm|wig |wapi|wapr|wapv|wapy|wapa|waps|wapt|winc|winw|wonu|x700|xda2|xdag|yas\-|your|zte\-|zeto|aste|audi|avan|blaz|brew|brvw|bumb|ccwa|cell|cldc|cmd\-|dang|eml2|fetc|hipt|http|ibro|idea|ikom|ipaq|jbro|jemu|jigs|keji|kyoc|kyok|libw|m\-cr|midp|mmef|moto|mwbp|mywa|newt|nok6|o2im|pant|pdxg|play|pluc|port|prox|rozo|sama|seri|smal|symb|treo|upsi|vx52|vx53|vx60|vx61|vx70|vx80|vx81|vx83|vx85|wap\-|webc|whit|wmlb|xda\-|xda_)'
-    regex_flag: 'i'
-    device_replacement: 'Generic Feature Phone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Feature Phone'
-  - regex: '^(Ice)$'
-    device_replacement: 'Generic Feature Phone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Feature Phone'
-  - regex: '(wap[\-\ ]browser|maui|netfront|obigo|teleca|up\.browser|midp|Opera Mini)'
-    regex_flag: 'i'
-    device_replacement: 'Generic Feature Phone'
-    brand_replacement: 'Generic'
-    model_replacement: 'Feature Phone'

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/doc/ruby-user-agent-parser/changelog.gz
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/user_agent_parser-2.15.0.gemspec

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/user_agent_parser-2.5.1.gemspec

No differences were encountered in the control files

More details

Full run details