From 58c88c1b2980ee1c02a47cb3d35de1480402e6ac Mon Sep 17 00:00:00 2001
From: mattab <matthieu.aubry@gmail.com>
Date: Fri, 24 May 2013 23:23:17 +1200
Subject: [PATCH] Refs #3612  * making tests pass!  * enabling DevicesDetection
 tests, even though DevicesDetection plugin is disabled by default.

---
 core/Tracker.php                              |   30 +-
 libs/UserAgentParser/UserAgentParser.php      |    9 +
 plugins/DevicesDetection/DevicesDetection.php |    4 +-
 .../UserAgentParserEnhanced.php               | 1455 ++++++------
 .../regexes/browsers.yml                      |  814 +++----
 .../regexes/mobiles.yml                       | 1914 +++++++--------
 .../UserAgentParserEnhanced/regexes/oss.yml   |  852 +++----
 .../UserAgentParserEnhanced/spyc.php          | 2090 ++++++++---------
 plugins/DevicesDetection/functions.php        |   12 +-
 plugins/DevicesDetection/lang/en.php          |    2 +-
 tests/LocalTracker.php                        |    1 +
 .../Core/PluginsFunctions/WidgetsListTest.php |    8 +-
 tests/PHPUnit/Core/SegmentTest.php            |    5 +-
 tests/PHPUnit/IntegrationTestCase.php         |    2 +-
 tests/PHPUnit/proxy/piwik.php                 |    7 +
 15 files changed, 3622 insertions(+), 3583 deletions(-)

diff --git a/core/Tracker.php b/core/Tracker.php
index 89fa433836..625d7d2175 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -46,6 +46,7 @@ class Piwik_Tracker
     static protected $forcedVisitorId = null;
 
     static protected $pluginsNotToLoad = array();
+    static protected $pluginsToLoad = array();
 
     /**
      * The set of visits to track.
@@ -126,6 +127,17 @@ class Piwik_Tracker
         return self::$pluginsNotToLoad;
     }
 
+    static public function getPluginsToLoad()
+    {
+        return self::$pluginsToLoad;
+    }
+    static public function setPluginsToLoad($plugins)
+    {
+        self::$pluginsToLoad = $plugins;
+    }
+
+
+
     /**
      * Update Tracker config
      *
@@ -357,6 +369,7 @@ class Piwik_Tracker
             $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
             $pluginsForcedNotToLoad = Piwik_Tracker::getPluginsNotToLoad();
             $pluginsToLoad = array_diff($pluginsToLoad, $pluginsForcedNotToLoad);
+            $pluginsToLoad = array_merge($pluginsToLoad, Piwik_Tracker::getPluginsToLoad());
             $pluginsManager->loadPlugins($pluginsToLoad);
         }
     }
@@ -589,15 +602,15 @@ class Piwik_Tracker
         }
 
         try {
-            $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker;
-            if (is_array($pluginsTracker)
-                && count($pluginsTracker) != 0
-            ) {
-                $pluginsTracker['Plugins_Tracker'] = array_diff($pluginsTracker['Plugins_Tracker'], self::getPluginsNotToLoad());
+            $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
+            if (count($pluginsTracker) > 0) {
+                $pluginsTracker = $pluginsTracker;
+                $pluginsTracker = array_diff($pluginsTracker, self::getPluginsNotToLoad());
                 Piwik_PluginsManager::getInstance()->doNotLoadAlwaysActivatedPlugins();
-                Piwik_PluginsManager::getInstance()->loadPlugins($pluginsTracker['Plugins_Tracker']);
 
-                printDebug("Loading plugins: { " . implode(",", $pluginsTracker['Plugins_Tracker']) . " }");
+                Piwik_PluginsManager::getInstance()->loadPlugins($pluginsTracker);
+
+                printDebug("Loading plugins: { " . implode(",", $pluginsTracker) . " }");
             }
         } catch (Exception $e) {
             printDebug("ERROR: " . $e->getMessage());
@@ -757,6 +770,9 @@ class Piwik_Tracker
 
         // Disable provider plugin, because it is so slow to do reverse ip lookup in dev environment somehow
         self::setPluginsNotToLoad($pluginsDisabled);
+
+        // we load 'DevicesDetection' in tests only (disabled by default)
+        self::setPluginsToLoad( array('DevicesDetection') );
     }
 }
 
diff --git a/libs/UserAgentParser/UserAgentParser.php b/libs/UserAgentParser/UserAgentParser.php
index c65ed056f6..85c9225cc5 100644
--- a/libs/UserAgentParser/UserAgentParser.php
+++ b/libs/UserAgentParser/UserAgentParser.php
@@ -653,6 +653,11 @@ class UserAgentParser
         if (isset(self::$browserIdToName[$browserId])) {
             return self::$browserIdToName[$browserId];
         }
+        if(class_exists('UserAgentParserEnhanced')) {
+            if( !empty(UserAgentParserEnhanced::$browsers[$browserId])) {
+                return UserAgentParserEnhanced::$browsers[$browserId];
+            }
+        }
         return false;
     }
 
@@ -684,6 +689,10 @@ class UserAgentParser
         if (isset(self::$operatingSystemsIdToName[$osId])) {
             return self::$operatingSystemsIdToName[$osId];
         }
+
+        if(class_exists('UserAgentParserEnhanced')) {
+            return UserAgentParserEnhanced::getOsNameFromId($osId);
+        }
         return false;
     }
 
diff --git a/plugins/DevicesDetection/DevicesDetection.php b/plugins/DevicesDetection/DevicesDetection.php
index e9cb732e8c..d18f768b21 100644
--- a/plugins/DevicesDetection/DevicesDetection.php
+++ b/plugins/DevicesDetection/DevicesDetection.php
@@ -21,8 +21,8 @@ class Piwik_DevicesDetection extends Piwik_Plugin
     public function getInformation()
     {
         return array(
-            'description' => Piwik_Translate("DevicesDetection_description"),
-            'author' => 'Clearcode.cc',
+            'description' => "[Beta Plugin] " . Piwik_Translate("DevicesDetection_description"),
+            'author' => 'Piwik and Clearcode.cc',
             'author_homepage' => 'http://clearcode.cc',
             'version' => '1.12-b6',
             'TrackerPlugin' => true,
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php b/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php
index 9031b0fd33..f107455ad9 100644
--- a/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php
@@ -1,722 +1,735 @@
-<?php
-
-/**
- * Piwik - Open source web analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- * @category Piwik_Plugins
- * @package Piwik_DevicesDetection
- */
-//yml parser
-require_once('spyc.php');
-
-class UserAgentParserEnhanced
-{
-
-    public static $deviceTypes = array('car browser', 'console', 'desktop', 'feature phone', 'smartphone', 'tablet', 'tv');
-    public static $deviceBrands = array(
-        'AC' => 'Acer',
-        'AI' => 'Airness',
-        'AL' => 'Alcatel',
-        'AO' => 'Amoi',
-        'AP' => 'Apple',
-        'AU' => 'Asus',
-        'AV' => 'Avvio',
-        'AX' => 'Audiovox',
-        'BE' => 'Becker',
-        'BI' => 'Bird',
-        'BL' => 'Beetel',
-        'BQ' => 'BenQ',
-        'BS' => 'BenQ-Siemens',
-        'CK' => 'Cricket',
-        'CL' => 'Compal',
-        'CT' => 'Capitel',
-        'DB' => 'Dbtel',
-        'DC' => 'DoCoMo',
-        'DI' => 'Dicam',
-        'DL' => 'Dell',
-        'DP' => 'Dopod',
-        'EC' => 'Ericsson',
-        'EI' => 'Ezio',
-        'ER' => 'Ericy',
-        'ET' => 'eTouch',
-        'EZ' => 'Ezze',
-        'FL' => 'Fly',
-        'GI' => 'Gionee',
-        'GO' => 'Google',
-        'GR' => 'Gradiente',
-        'GU' => 'Grundig',
-        'HA' => 'Haier',
-        'HP' => 'HP',
-        'HT' => 'HTC',
-        'HU' => 'Huawei',
-        'IK' => 'iKoMo',
-        'IM' => 'i-mate',
-        'IN' => 'Innostream',
-        'IO' => 'i-mobile',
-        'IQ' => 'INQ',
-        'KA' => 'Karbonn',
-        'KD' => 'KDDI',
-        'KN' => 'Kindle',
-        'KO' => 'Konka',
-        'KY' => 'Kyocera',
-        'LA' => 'Lanix',
-        'LC' => 'LCT',
-        'LE' => 'Lenovo',
-        'LG' => 'LG',
-        'LU' => 'LGUPlus',
-        'MI' => 'MicroMax',
-        'MO' => 'Mio',
-        'MR' => 'Motorola',
-        'MS' => 'Microsoft',
-        'MT' => 'Mitsubishi',
-        'MY' => 'MyPhone',
-        'NE' => 'NEC',
-        'NG' => 'NGM',
-        'NI' => 'Nintendo',
-        'NK' => 'Nokia',
-        'NW' => 'Newgen',
-        'NX' => 'Nexian',
-        'OD' => 'Onda',
-        'OP' => 'OPPO',
-        'OR' => 'Orange',
-        'OT' => 'O2',
-        'PA' => 'Panasonic',
-        'PH' => 'Philips',
-        'PM' => 'Palm',
-        'PO' => 'phoneOne',
-        'PT' => 'Pantech',
-        'QT' => 'Qtek',
-        'RM' => 'RIM',
-        'RO' => 'Rover',
-        'SA' => 'Samsung',
-        'SD' => 'Sega',
-        'SE' => 'Sony Ericsson',
-        'SF' => 'Softbank',
-        'SG' => 'Sagem',
-        'SH' => 'Sharp',
-        'SI' => 'Siemens',
-        'SN' => 'Sendo',
-        'SO' => 'Sony',
-        'SP' => 'Spice',
-        'SY' => 'Sanyo',
-        'TA' => 'Tesla',
-        'TC' => 'TCL',
-        'TE' => 'Telit',
-        'TH' => 'TiPhone',
-        'TI' => 'TIANYU',
-        'TM' => 'T-Mobile',
-        'TO' => 'Toplux',
-        'TS' => 'Toshiba',
-        'UT' => 'UTStarcom',
-        'VD' => 'Videocon',
-        'VE' => 'Vertu',
-        'VI' => 'Vitelcom',
-        'VK' => 'VK Mobile',
-        'VO' => 'Voxtel',
-        'WB' => 'Web TV',
-        'WE' => 'WellcoM',
-        'WO' => 'Wonu',
-        'XX' => 'Unknown',
-        'ZO' => 'Zonda',
-        'ZT' => 'ZTE',
-    );
-    public static $osShorts = array(
-        'AIX' => 'AIX',
-        'Android' => 'AND',
-        'Apple TV' => 'ATV',
-        'Arch Linux' => 'ARL',
-        'BackTrack' => 'BTR',
-        'Bada' => 'SBA',
-        'BlackBerry OS' => 'BLB',
-        'BlackBerry Tablet OS' => 'QNX',
-        'Bot' => 'BOT',
-        'Brew' => 'BMP',
-        'CentOS' => 'CES',
-        'Chrome OS' => 'COS',
-        'Debian' => 'DEB',
-        'DragonFly' => 'DFB',
-        'Fedora' => 'FED',
-        'Firefox OS' => 'FOS',
-        'FreeBSD' => 'BSD',
-        'Gentoo' => 'GNT',
-        'Google TV' => 'GTV',
-        'HP-UX' => 'HPX',
-        'IRIX' => 'IRI',
-        'Knoppix' => 'KNO',
-        'Kubuntu' => 'KBT',
-        'Linux' => 'LIN',
-        'Lubuntu' => 'LBT',
-        'Mac' => 'MAC',
-        'Mandriva' => 'MDR',
-        'MeeGo' => 'SMG',
-        'Mint' => 'MIN',
-        'NetBSD' => 'NBS',
-        'Nintendo' => 'WII',
-        'Nintendo Mobile' => 'NDS',
-        'OS/2' => 'OS2',
-        'OSF1' => 'T64',
-        'OpenBSD' => 'OBS',
-        'PlayStation' => 'PSP',
-        'PlayStation 3' => 'PS3',
-        'Presto' => 'PRS',
-        'Puppy' => 'PPY',
-        'Red Hat' => 'RHT',
-        'SUSE' => 'SSE',
-        'Slackware' => 'SLW',
-        'Solaris' => 'SOS',
-        'Syllable' => 'SYL',
-        'Symbian' => 'SYM',
-        'Symbian OS' => 'SYS',
-        'Symbian OS Series 40' => 'S40',
-        'Symbian OS Series 60' => 'S60',
-        'Symbian^3' => 'SY3',
-        'Talkatone' => 'TKT',
-        'Tizen' => 'TIZ',
-        'Ubuntu' => 'UBT',
-        'WebTV' => 'WTV',
-        'WinWAP' => 'WWP',
-        'Windows' => 'WIN',
-        'Windows 2000' => 'W2K',
-        'Windows 3.1' => 'W31',
-        'Windows 7' => 'WI7',
-        'Windows 8' => 'WI8',
-        'Windows 95' => 'W95',
-        'Windows 98' => 'W98',
-        'Windows CE' => 'WCE',
-        'Windows ME' => 'WME',
-        'Windows Mobile' => 'WMO',
-        'Windows NT' => 'WNT',
-        'Windows Phone' => 'WPH',
-        'Windows RT' => 'WRT',
-        'Windows Server 2003' => 'WS3',
-        'Windows Vista' => 'WVI',
-        'Windows XP' => 'WXP',
-        'Xbox' => 'XBX',
-        'Xubuntu' => 'XBT',
-        'YunOs' => 'YNS',
-        'iOS' => 'IOS',
-        'palmOS' => 'POS',
-        'webOS' => 'WOS'
-    );
-    protected static $desktopOsArray = array('IBM', 'Linux', 'Mac', 'Unix', 'Windows');
-    public static $osFamilies = array(
-        'Android' => array('AND'),
-        'Apple TV' => array('ATV'),
-        'BlackBerry' => array('BLB'),
-        'Bot' => array('BOT'),
-        'Brew' => array('BMP'),
-        'Chrome OS' => array('COS'),
-        'Firefox OS' => array('FOS'),
-        'Gaming Console' => array('WII', 'PS3'),
-        'Google TV' => array('GTV'),
-        'IBM' => array('OS2'),
-        'iOS' => array('IOS'),
-        'Linux' => array('LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 'RHT', 'MDR', 'GNT', 'SLW', 'SSE', 'PPY', 'CES', 'BTR', 'YNS', 'PRS'),
-        'Mac' => array('MAC'),
-        'Mobile Gaming Console' => array('PSP', 'NDS', 'XBX'),
-        'Other Mobile' => array('WOS', 'POS', 'QNX', 'SBA', 'TIZ'),
-        'Simulator' => array('TKT', 'WWP'),
-        'Symbian' => array('SYM', 'SYS', 'SY3', 'S60', 'S40', 'SMG'),
-        'Unix' => array('SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
-        'WebTV' => array('WTV'),
-        'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95', 'WRT', 'W31', 'WIN'),
-        'Windows Mobile' => array('WPH', 'WMO', 'WCE')
-    );
-    public static $browserFamilies = array(
-        'Android Browser' => array('AN'),
-        'BlackBerry Browser' => array('BB'),
-        'Chrome' => array('CH', 'CM', 'CI', 'CF', 'CR', 'RM'),
-        'Firefox' => array('FF', 'FE', 'SX', 'FB', 'PX', 'MB'),
-        'Internet Explorer' => array('IE', 'IM'),
-        'Konqueror' => array('KO'),
-        'NetFront' => array('NF'),
-        'Nokia Browser' => array('NB'),
-        'Opera' => array('OP', 'OM', 'OI'),
-        'Safari' => array('SF', 'MF')
-    );
-    public static $browsers = array(
-        'AB' => 'ABrowse',
-        'AM' => 'Amaya',
-        'AN' => 'Android Browser',
-        'AR' => 'Arora',
-        'AV' => 'Amiga Voyager',
-        'AW' => 'Amiga Aweb',
-        'BB' => 'BlackBerry Browser',
-        'BD' => 'Baidu Browser',
-        'BE' => 'Beonex',
-        'BX' => 'BrowseX',
-        'CA' => 'Camino',
-        'CF' => 'Chrome Frame',
-        'CH' => 'Chrome',
-        'CI' => 'Chrome Mobile iOS',
-        'CK' => 'Conkeror',
-        'CM' => 'Chrome Mobile',
-        'CO' => 'CometBird',
-        'CR' => 'Chromium',
-        'CS' => 'Cheshire',
-        'DF' => 'Dolphin',
-        'DI' => 'Dillo',
-        'EL' => 'Elinks',
-        'EP' => 'Epiphany',
-        'FB' => 'Firebird',
-        'FD' => 'Fluid',
-        'FE' => 'Fennec',
-        'FF' => 'Firefox',
-        'FL' => 'Flock',
-        'FN' => 'Fireweb Navigator',
-        'GA' => 'Galeon',
-        'GE' => 'Google Earth',
-        'HJ' => 'HotJava',
-        'IB' => 'IBrowse',
-        'IC' => 'iCab',
-        'IE' => 'Internet Explorer',
-        'IM' => 'IE Mobile',
-        'IR' => 'Iron',
-        'JS' => 'Jasmine',
-        'KI' => 'Kindle Browser',
-        'KM' => 'K-meleon',
-        'KO' => 'Konqueror',
-        'KP' => 'Kapiko',
-        'KZ' => 'Kazehakase',
-        'LG' => 'Lightning',
-        'LI' => 'Links',
-        'LX' => 'Lynx',
-        'MB' => 'MicroB',
-        'MC' => 'NCSA Mosaic',
-        'MF' => 'Mobile Safari',
-        'MI' => 'Midori',
-        'MS' => 'Mobile Silk',
-        'MX' => 'Maxthon',
-        'NB' => 'Nokia Browser',
-        'NF' => 'NetFront',
-        'NL' => 'NetFront Life',
-        'NS' => 'Netscape',
-        'OB' => 'Obigo',
-        'OI' => 'Opera Mini',
-        'OM' => 'Opera Mobile',
-        'OP' => 'Opera',
-        'OV' => 'Openwave Mobile Browser',
-        'OW' => 'OmniWeb',
-        'PL' => 'Palm Blazer',
-        'PR' => 'Palm Pre',
-        'PX' => 'Phoenix',
-        'RK' => 'Rekonq',
-        'RM' => 'RockMelt',
-        'SF' => 'Safari',
-        'SM' => 'SeaMonkey',
-        'SN' => 'Snowshoe',
-        'SX' => 'Swiftfox',
-        'TZ' => 'Tizen Browser',
-        'UC' => 'UC Browser',
-        'WO' => 'wOSBrowser',
-        'YA' => 'Yandex Browser'
-    );
-
-    const UNKNOWN = "UNK";
-    protected static $regexesDir = '/regexes/';
-    protected static $osRegexesFile = 'oss.yml';
-    protected static $browserRegexesFile = 'browsers.yml';
-    protected static $mobileRegexesFile = 'mobiles.yml';
-    protected $userAgent;
-    protected $os;
-    protected $browser;
-    protected $device;
-    protected $brand;
-    protected $model;
-    protected $debug = false;
-
-    public function __construct($userAgent)
-    {
-        $this->userAgent = $userAgent;
-    }
-
-    protected function getOsRegexes()
-    {
-        return Spyc::YAMLLoad(__DIR__ . self::$regexesDir . self::$osRegexesFile);
-    }
-
-    protected function getBrowserRegexes()
-    {
-        return Spyc::YAMLLoad(__DIR__ . self::$regexesDir . self::$browserRegexesFile);
-    }
-
-    protected function getMobileRegexes()
-    {
-        return Spyc::YAMLLoad(__DIR__ . self::$regexesDir . self::$mobileRegexesFile);
-    }
-
-    public function parse()
-    {
-        $this->parseOs();
-        if ($this->isBot() || $this->isSimulator())
-            return;
-
-        $this->parseBrowser();
-
-        if ($this->isMobile()) {
-            $this->parseMobile();
-        } else {
-            $this->device = array_search('desktop', self::$deviceTypes);
-        }
-        if ($this->debug) {
-            var_dump($this->brand, $this->model, $this->device);
-        }
-    }
-
-    protected function parseOs()
-    {
-        foreach ($this->getOsRegexes() as $osRegex) {
-            $matches = $this->matchUserAgent($osRegex['regex']);
-            if ($matches)
-                break;
-        }
-
-        if (!$matches)
-            return;
-
-        if (in_array($osRegex['name'], self::$osShorts)) {
-            $short = self::$osShorts[$osRegex['name']];
-        } else {
-            $short = 'UNK';
-        }
-
-        $this->os = array(
-            'name' => $this->buildOsName($osRegex['name'], $matches),
-            'short_name' => $short,
-            'version' => $this->buildOsVersion($osRegex['version'], $matches)
-        );
-
-        if (array_key_exists($this->os['name'], self::$osShorts)) {
-            $this->os['short_name'] = self::$osShorts[$this->os['name']];
-        }
-    }
-
-    protected function parseBrowser()
-    {
-        foreach ($this->getBrowserRegexes() as $browserRegex) {
-            $matches = $this->matchUserAgent($browserRegex['regex']);
-            if ($matches)
-                break;
-        }
-
-        if (!$matches)
-            return;
-
-        if (in_array($browserRegex['name'], self::$browsers)) {
-            $short = array_search($browserRegex['name'], self::$browsers);
-        } else {
-            $short = 'XX';
-        }
-
-        $this->browser = array(
-            'name' => $this->buildBrowserName($browserRegex['name'], $matches),
-            'short_name' => $short,
-            'version' => $this->buildBrowserVersion($browserRegex['version'], $matches)
-        );
-    }
-
-    protected function parseMobile()
-    {
-        $mobileRegexes = $this->getMobileRegexes();
-        $this->parseBrand($mobileRegexes);
-        $this->parseModel($mobileRegexes);
-    }
-
-    protected function parseBrand($mobileRegexes)
-    {
-        foreach ($mobileRegexes as $brand => $mobileRegex) {
-            $matches = $this->matchUserAgent($mobileRegex['regex']);
-            if ($matches)
-                break;
-        }
-
-        if (!$matches)
-            return;
-        $this->brand = array_search($brand, self::$deviceBrands);
-        $this->fullName = $brand;
-
-        if (isset($mobileRegex['device'])) {
-            $this->device = array_search($mobileRegex['device'],self::$deviceTypes);
-        }
-
-        if (isset($mobileRegex['model'])) {
-            $this->model = $this->buildModel($mobileRegex['model'], $matches);
-        }
-    }
-
-    protected function parseModel($mobileRegexes)
-    {
-        if (empty($this->brand) || !empty($this->model))
-            return;
-
-        foreach ($mobileRegexes[$this->fullName]['models'] as $modelRegex) {
-            $matches = $this->matchUserAgent($modelRegex['regex']);
-            if ($matches)
-                break;
-        }
-
-        if (!$matches) {
-            return;
-        }
-
-        $this->model = $this->buildModel($modelRegex['model'], $matches);
-
-        if (isset($modelRegex['device'])) {
-            $this->device = array_search($modelRegex['device'], self::$deviceTypes);
-        }
-    }
-
-    protected function matchUserAgent($regex)
-    {
-        $regex = '/' . str_replace('/', '\/', $regex) . '/i';
-
-        if (preg_match($regex, $this->userAgent, $matches)) {
-            return $matches;
-        }
-
-        return false;
-    }
-
-    protected function buildOsName($osName, $matches)
-    {
-        return $this->buildByMatch($osName, $matches);
-    }
-
-    protected function buildOsVersion($osVersion, $matches)
-    {
-        $osVersion = $this->buildByMatch($osVersion, $matches);
-
-        $osVersion = $this->buildByMatch($osVersion, $matches, '2');
-
-        $osVersion = str_replace('_', '.', $osVersion);
-
-        return $osVersion;
-    }
-
-    protected function buildBrowserName($browserName, $matches)
-    {
-        return $this->buildByMatch($browserName, $matches);
-    }
-
-    protected function buildBrowserVersion($browserVersion, $matches)
-    {
-        $browserVersion = $this->buildByMatch($browserVersion, $matches);
-
-        $browserVersion = $this->buildByMatch($browserVersion, $matches, '2');
-
-        $browserVersion = str_replace('_', '.', $browserVersion);
-
-        return $browserVersion;
-    }
-
-    protected function buildModel($model, $matches)
-    {
-        $model = $this->buildByMatch($model, $matches);
-
-        $model = $this->buildByMatch($model, $matches, '2');
-
-        $model = $this->buildModelExceptions($model);
-
-        $model = str_replace('_', ' ', $model);
-
-        return $model;
-    }
-
-    protected function buildModelExceptions($model)
-    {
-        if ($this->brand == 'O2') {
-            $model = preg_replace('/([a-z])([A-Z])/', '$1 $2', $model);
-            $model = ucwords(str_replace('_', ' ', $model));
-        }
-
-        return $model;
-    }
-
-    /**
-     * This method is used in this class for processing results of pregmatch 
-     * results into string containing recognized information.
-     * 
-     * General algorithm:
-     * Parsing UserAgent string consists of trying to match it against list of
-     * regular expressions for three different information: 
-     * browser + version,
-     * OS + version, 
-     * device manufacturer + model. 
-     * 
-     * After match has been found iteration stops, and results are processed 
-     * by buildByMatch.
-     * As $item we get decoded name (name of browser, name of OS, name of manufacturer).
-     * In array $match we recieve preg_match results containing whole string matched at index 0
-     * and following matches in further indexes. Desired action now is to concatenate
-     * decoded name ($item) with matches found. First step is to append first found match,
-     * which is located in index=1 (that's why $nb is 1 by default).
-     * In other cases, where whe know that preg_match may return more than 1 result,
-     * we call buildByMatch with $nb = 2 or more, depending on what will be returned from
-     * regular expression.
-     * 
-     * Example:
-     * We are parsing UserAgent of Firefox 20.0 browser. 
-     * UserAgentParserEnhanced calls buildBrowserName() and buildBrowserVersion() in order
-     * to retrieve those information.
-     * In buildBrowserName() we only have one call of buildByMatch, where passed argument
-     * is regular expression testing given string for browser name. In this case, we are only
-     * interrested in first hit, so no $nb parameter will be set to 1. After finding match, and calling
-     * buildByMatch - we will receive just the name of browser.
-     * 
-     * Also after decoding browser we will get list of regular expressions for this browser name
-     * testing UserAgent string for version number. Again we iterate over this list, and after finding first
-     * occurence - we break loop and proceed to build by match. Since browser regular expressions can
-     * contain two hits (major version and minor version) in function buildBrowserVersion() we have
-     * two calls to buildByMatch, one without 3rd parameter, and second with $nb set to 2.
-     * This way we can retrieve version number, and assign it to object property.
-     * 
-     * In case of mobiles.yml this schema slightly varies, but general idea is the same.
-     * 
-     * @param string $item
-     * @param array $matches
-     * @param int $nb
-     * @return type
-     */
-    protected function buildByMatch($item, $matches, $nb = '1')
-    {
-        if (strpos($item, '$' . $nb) === false)
-            return $item;
-
-        $replace = isset($matches[$nb]) ? $matches[$nb] : '';
-        return trim(str_replace('$' . $nb, $replace, $item));
-    }
-
-    public function isBot()
-    {
-        $decodedFamily = '';
-        if (in_array($this->getOs('name'), self::$osShorts)) {
-            $osShort = self::$osShorts[$this->getOs('name')];
-        } else {
-            $osShort = '';
-        }
-        foreach (self::$osFamilies as $family => $familyOs) {
-            if (in_array($osShort, $familyOs)) {
-                $decodedFamily = $family;
-                break;
-            }
-        }
-
-        return $decodedFamily == 'Bot';
-    }
-
-    public function isSimulator()
-    {
-        $decodedFamily = '';
-        if (in_array($this->getOs('name'), self::$osShorts)) {
-            $osShort = self::$osShorts[$this->getOs('name')];
-        } else {
-            $osShort = '';
-        }
-        foreach (self::$osFamilies as $family => $familyOs) {
-            if (in_array($osShort, $familyOs)) {
-                $decodedFamily = $family;
-                break;
-            }
-        }
-        return $decodedFamily == 'Simulator';
-    }
-
-    public function isMobile()
-    {
-        return !$this->isDesktop();
-    }
-
-    public function isDesktop()
-    {
-        $osName = $this->getOs('name');
-        if (empty($osName) || empty(self::$osShorts[$osName])) {
-            return false;
-        }
-
-        $osShort = self::$osShorts[$osName];
-        foreach (self::$osFamilies as $family => $familyOs) {
-            if (in_array($osShort, $familyOs)) {
-                $decodedFamily = $family;
-                break;
-            }
-        }
-        return in_array($decodedFamily, self::$desktopOsArray);
-    }
-
-    public function getOs($attr = '')
-    {
-        if ($attr == '') {
-            return $this->os;
-        }
-
-        if (!isset($this->os[$attr])) {
-            return self::UNKNOWN;
-        }
-
-        if ($attr == 'version') {
-            $this->os['version'] = $this->os['version'];
-        }
-        return $this->os[$attr];
-    }
-
-    public function getBrowser($attr = '')
-    {
-        if ($attr == '') {
-            return $this->browser;
-        }
-
-        if (!isset($this->browser[$attr])) {
-            return self::UNKNOWN;
-        }
-
-        return $this->browser[$attr];
-    }
-
-    public function getDevice()
-    {
-        return $this->device;
-    }
-
-    public function getBrand()
-    {
-        return $this->brand;
-    }
-
-    public function getModel()
-    {
-        return $this->model;
-    }
-
-    public function getUserAgent()
-    {
-        return $this->userAgent;
-    }
-
-    public static function getOsFamily($osLabel)
-    {
-        $osShortName = substr($osLabel, 0, 3);
-
-        foreach (self::$osFamilies as $osFamily => $osShortNames) {
-            if (in_array($osShortName, $osShortNames)) {
-                return $osFamily;
-            }
-        }
-
-        return 'Other';
-    }
-
-    public static function getBrowserFamily($browserLabel)
-    {
-        foreach (self::$browserFamilies as $browserFamily => $browserShortNames) {
-            if (in_array($browserLabel, $browserShortNames)) {
-                return $browserFamily;
-            }
-        }
-
-        return 'Other';
-    }
-
+<?php
+
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+//yml parser
+require_once('spyc.php');
+
+class UserAgentParserEnhanced
+{
+
+    public static $deviceTypes = array('car browser', 'console', 'desktop', 'feature phone', 'smartphone', 'tablet', 'tv');
+    public static $deviceBrands = array(
+        'AC' => 'Acer',
+        'AI' => 'Airness',
+        'AL' => 'Alcatel',
+        'AO' => 'Amoi',
+        'AP' => 'Apple',
+        'AU' => 'Asus',
+        'AV' => 'Avvio',
+        'AX' => 'Audiovox',
+        'BE' => 'Becker',
+        'BI' => 'Bird',
+        'BL' => 'Beetel',
+        'BQ' => 'BenQ',
+        'BS' => 'BenQ-Siemens',
+        'CK' => 'Cricket',
+        'CL' => 'Compal',
+        'CT' => 'Capitel',
+        'DB' => 'Dbtel',
+        'DC' => 'DoCoMo',
+        'DI' => 'Dicam',
+        'DL' => 'Dell',
+        'DP' => 'Dopod',
+        'EC' => 'Ericsson',
+        'EI' => 'Ezio',
+        'ER' => 'Ericy',
+        'ET' => 'eTouch',
+        'EZ' => 'Ezze',
+        'FL' => 'Fly',
+        'GI' => 'Gionee',
+        'GO' => 'Google',
+        'GR' => 'Gradiente',
+        'GU' => 'Grundig',
+        'HA' => 'Haier',
+        'HP' => 'HP',
+        'HT' => 'HTC',
+        'HU' => 'Huawei',
+        'IK' => 'iKoMo',
+        'IM' => 'i-mate',
+        'IN' => 'Innostream',
+        'IO' => 'i-mobile',
+        'IQ' => 'INQ',
+        'KA' => 'Karbonn',
+        'KD' => 'KDDI',
+        'KN' => 'Kindle',
+        'KO' => 'Konka',
+        'KY' => 'Kyocera',
+        'LA' => 'Lanix',
+        'LC' => 'LCT',
+        'LE' => 'Lenovo',
+        'LG' => 'LG',
+        'LU' => 'LGUPlus',
+        'MI' => 'MicroMax',
+        'MO' => 'Mio',
+        'MR' => 'Motorola',
+        'MS' => 'Microsoft',
+        'MT' => 'Mitsubishi',
+        'MY' => 'MyPhone',
+        'NE' => 'NEC',
+        'NG' => 'NGM',
+        'NI' => 'Nintendo',
+        'NK' => 'Nokia',
+        'NW' => 'Newgen',
+        'NX' => 'Nexian',
+        'OD' => 'Onda',
+        'OP' => 'OPPO',
+        'OR' => 'Orange',
+        'OT' => 'O2',
+        'PA' => 'Panasonic',
+        'PH' => 'Philips',
+        'PM' => 'Palm',
+        'PO' => 'phoneOne',
+        'PT' => 'Pantech',
+        'QT' => 'Qtek',
+        'RM' => 'RIM',
+        'RO' => 'Rover',
+        'SA' => 'Samsung',
+        'SD' => 'Sega',
+        'SE' => 'Sony Ericsson',
+        'SF' => 'Softbank',
+        'SG' => 'Sagem',
+        'SH' => 'Sharp',
+        'SI' => 'Siemens',
+        'SN' => 'Sendo',
+        'SO' => 'Sony',
+        'SP' => 'Spice',
+        'SY' => 'Sanyo',
+        'TA' => 'Tesla',
+        'TC' => 'TCL',
+        'TE' => 'Telit',
+        'TH' => 'TiPhone',
+        'TI' => 'TIANYU',
+        'TM' => 'T-Mobile',
+        'TO' => 'Toplux',
+        'TS' => 'Toshiba',
+        'UT' => 'UTStarcom',
+        'VD' => 'Videocon',
+        'VE' => 'Vertu',
+        'VI' => 'Vitelcom',
+        'VK' => 'VK Mobile',
+        'VO' => 'Voxtel',
+        'WB' => 'Web TV',
+        'WE' => 'WellcoM',
+        'WO' => 'Wonu',
+        'XX' => 'Unknown',
+        'ZO' => 'Zonda',
+        'ZT' => 'ZTE',
+    );
+    public static $osShorts = array(
+        'AIX' => 'AIX',
+        'Android' => 'AND',
+        'Apple TV' => 'ATV',
+        'Arch Linux' => 'ARL',
+        'BackTrack' => 'BTR',
+        'Bada' => 'SBA',
+        'BlackBerry OS' => 'BLB',
+        'BlackBerry Tablet OS' => 'QNX',
+        'Bot' => 'BOT',
+        'Brew' => 'BMP',
+        'CentOS' => 'CES',
+        'Chrome OS' => 'COS',
+        'Debian' => 'DEB',
+        'DragonFly' => 'DFB',
+        'Fedora' => 'FED',
+        'Firefox OS' => 'FOS',
+        'FreeBSD' => 'BSD',
+        'Gentoo' => 'GNT',
+        'Google TV' => 'GTV',
+        'HP-UX' => 'HPX',
+        'IRIX' => 'IRI',
+        'Knoppix' => 'KNO',
+        'Kubuntu' => 'KBT',
+        'Linux' => 'LIN',
+        'Lubuntu' => 'LBT',
+        'Mac' => 'MAC',
+        'Mandriva' => 'MDR',
+        'MeeGo' => 'SMG',
+        'Mint' => 'MIN',
+        'NetBSD' => 'NBS',
+        'Nintendo' => 'WII',
+        'Nintendo Mobile' => 'NDS',
+        'OS/2' => 'OS2',
+        'OSF1' => 'T64',
+        'OpenBSD' => 'OBS',
+        'PlayStation' => 'PSP',
+        'PlayStation 3' => 'PS3',
+        'Presto' => 'PRS',
+        'Puppy' => 'PPY',
+        'Red Hat' => 'RHT',
+        'SUSE' => 'SSE',
+        'Slackware' => 'SLW',
+        'Solaris' => 'SOS',
+        'Syllable' => 'SYL',
+        'Symbian' => 'SYM',
+        'Symbian OS' => 'SYS',
+        'Symbian OS Series 40' => 'S40',
+        'Symbian OS Series 60' => 'S60',
+        'Symbian^3' => 'SY3',
+        'Talkatone' => 'TKT',
+        'Tizen' => 'TIZ',
+        'Ubuntu' => 'UBT',
+        'WebTV' => 'WTV',
+        'WinWAP' => 'WWP',
+        'Windows' => 'WIN',
+        'Windows 2000' => 'W2K',
+        'Windows 3.1' => 'W31',
+        'Windows 7' => 'WI7',
+        'Windows 8' => 'WI8',
+        'Windows 95' => 'W95',
+        'Windows 98' => 'W98',
+        'Windows CE' => 'WCE',
+        'Windows ME' => 'WME',
+        'Windows Mobile' => 'WMO',
+        'Windows NT' => 'WNT',
+        'Windows Phone' => 'WPH',
+        'Windows RT' => 'WRT',
+        'Windows Server 2003' => 'WS3',
+        'Windows Vista' => 'WVI',
+        'Windows XP' => 'WXP',
+        'Xbox' => 'XBX',
+        'Xubuntu' => 'XBT',
+        'YunOs' => 'YNS',
+        'iOS' => 'IOS',
+        'palmOS' => 'POS',
+        'webOS' => 'WOS'
+    );
+    protected static $desktopOsArray = array('IBM', 'Linux', 'Mac', 'Unix', 'Windows');
+    public static $osFamilies = array(
+        'Android' => array('AND'),
+        'Apple TV' => array('ATV'),
+        'BlackBerry' => array('BLB'),
+        'Bot' => array('BOT'),
+        'Brew' => array('BMP'),
+        'Chrome OS' => array('COS'),
+        'Firefox OS' => array('FOS'),
+        'Gaming Console' => array('WII', 'PS3'),
+        'Google TV' => array('GTV'),
+        'IBM' => array('OS2'),
+        'iOS' => array('IOS'),
+        'Linux' => array('LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 'RHT', 'MDR', 'GNT', 'SLW', 'SSE', 'PPY', 'CES', 'BTR', 'YNS', 'PRS'),
+        'Mac' => array('MAC'),
+        'Mobile Gaming Console' => array('PSP', 'NDS', 'XBX'),
+        'Other Mobile' => array('WOS', 'POS', 'QNX', 'SBA', 'TIZ'),
+        'Simulator' => array('TKT', 'WWP'),
+        'Symbian' => array('SYM', 'SYS', 'SY3', 'S60', 'S40', 'SMG'),
+        'Unix' => array('SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
+        'WebTV' => array('WTV'),
+        'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95', 'WRT', 'W31', 'WIN'),
+        'Windows Mobile' => array('WPH', 'WMO', 'WCE')
+    );
+    public static $browserFamilies = array(
+        'Android Browser' => array('AN'),
+        'BlackBerry Browser' => array('BB'),
+        'Chrome' => array('CH', 'CM', 'CI', 'CF', 'CR', 'RM'),
+        'Firefox' => array('FF', 'FE', 'SX', 'FB', 'PX', 'MB'),
+        'Internet Explorer' => array('IE', 'IM'),
+        'Konqueror' => array('KO'),
+        'NetFront' => array('NF'),
+        'Nokia Browser' => array('NB'),
+        'Opera' => array('OP', 'OM', 'OI'),
+        'Safari' => array('SF', 'MF')
+    );
+    public static $browsers = array(
+        'AB' => 'ABrowse',
+        'AM' => 'Amaya',
+        'AN' => 'Android Browser',
+        'AR' => 'Arora',
+        'AV' => 'Amiga Voyager',
+        'AW' => 'Amiga Aweb',
+        'BB' => 'BlackBerry Browser',
+        'BD' => 'Baidu Browser',
+        'BE' => 'Beonex',
+        'BX' => 'BrowseX',
+        'CA' => 'Camino',
+        'CF' => 'Chrome Frame',
+        'CH' => 'Chrome',
+        'CI' => 'Chrome Mobile iOS',
+        'CK' => 'Conkeror',
+        'CM' => 'Chrome Mobile',
+        'CO' => 'CometBird',
+        'CR' => 'Chromium',
+        'CS' => 'Cheshire',
+        'DF' => 'Dolphin',
+        'DI' => 'Dillo',
+        'EL' => 'Elinks',
+        'EP' => 'Epiphany',
+        'FB' => 'Firebird',
+        'FD' => 'Fluid',
+        'FE' => 'Fennec',
+        'FF' => 'Firefox',
+        'FL' => 'Flock',
+        'FN' => 'Fireweb Navigator',
+        'GA' => 'Galeon',
+        'GE' => 'Google Earth',
+        'HJ' => 'HotJava',
+        'IB' => 'IBrowse',
+        'IC' => 'iCab',
+        'IE' => 'Internet Explorer',
+        'IM' => 'IE Mobile',
+        'IR' => 'Iron',
+        'JS' => 'Jasmine',
+        'KI' => 'Kindle Browser',
+        'KM' => 'K-meleon',
+        'KO' => 'Konqueror',
+        'KP' => 'Kapiko',
+        'KZ' => 'Kazehakase',
+        'LG' => 'Lightning',
+        'LI' => 'Links',
+        'LX' => 'Lynx',
+        'MB' => 'MicroB',
+        'MC' => 'NCSA Mosaic',
+        'MF' => 'Mobile Safari',
+        'MI' => 'Midori',
+        'MS' => 'Mobile Silk',
+        'MX' => 'Maxthon',
+        'NB' => 'Nokia Browser',
+        'NF' => 'NetFront',
+        'NL' => 'NetFront Life',
+        'NS' => 'Netscape',
+        'OB' => 'Obigo',
+        'OI' => 'Opera Mini',
+        'OM' => 'Opera Mobile',
+        'OP' => 'Opera',
+        'OV' => 'Openwave Mobile Browser',
+        'OW' => 'OmniWeb',
+        'PL' => 'Palm Blazer',
+        'PR' => 'Palm Pre',
+        'PX' => 'Phoenix',
+        'RK' => 'Rekonq',
+        'RM' => 'RockMelt',
+        'SF' => 'Safari',
+        'SM' => 'SeaMonkey',
+        'SN' => 'Snowshoe',
+        'SX' => 'Swiftfox',
+        'TZ' => 'Tizen Browser',
+        'UC' => 'UC Browser',
+        'WO' => 'wOSBrowser',
+        'YA' => 'Yandex Browser'
+    );
+
+    const UNKNOWN = "UNK";
+    protected static $regexesDir = '/regexes/';
+    protected static $osRegexesFile = 'oss.yml';
+    protected static $browserRegexesFile = 'browsers.yml';
+    protected static $mobileRegexesFile = 'mobiles.yml';
+    protected $userAgent;
+    protected $os;
+    protected $browser;
+    protected $device;
+    protected $brand;
+    protected $model;
+    protected $debug = false;
+
+    public function __construct($userAgent)
+    {
+        $this->userAgent = $userAgent;
+    }
+
+    protected function getOsRegexes()
+    {
+        return Spyc::YAMLLoad(__DIR__ . self::$regexesDir . self::$osRegexesFile);
+    }
+
+    protected function getBrowserRegexes()
+    {
+        return Spyc::YAMLLoad(__DIR__ . self::$regexesDir . self::$browserRegexesFile);
+    }
+
+    protected function getMobileRegexes()
+    {
+        return Spyc::YAMLLoad(__DIR__ . self::$regexesDir . self::$mobileRegexesFile);
+    }
+
+    public function parse()
+    {
+        $this->parseOs();
+        if ($this->isBot() || $this->isSimulator())
+            return;
+
+        $this->parseBrowser();
+
+        if ($this->isMobile()) {
+            $this->parseMobile();
+        } else {
+            $this->device = array_search('desktop', self::$deviceTypes);
+        }
+        if ($this->debug) {
+            var_dump($this->brand, $this->model, $this->device);
+        }
+    }
+
+    protected function parseOs()
+    {
+        foreach ($this->getOsRegexes() as $osRegex) {
+            $matches = $this->matchUserAgent($osRegex['regex']);
+            if ($matches)
+                break;
+        }
+
+        if (!$matches)
+            return;
+
+        if (in_array($osRegex['name'], self::$osShorts)) {
+            $short = self::$osShorts[$osRegex['name']];
+        } else {
+            $short = 'UNK';
+        }
+
+        $this->os = array(
+            'name' => $this->buildOsName($osRegex['name'], $matches),
+            'short_name' => $short,
+            'version' => $this->buildOsVersion($osRegex['version'], $matches)
+        );
+
+        if (array_key_exists($this->os['name'], self::$osShorts)) {
+            $this->os['short_name'] = self::$osShorts[$this->os['name']];
+        }
+    }
+
+    protected function parseBrowser()
+    {
+        foreach ($this->getBrowserRegexes() as $browserRegex) {
+            $matches = $this->matchUserAgent($browserRegex['regex']);
+            if ($matches)
+                break;
+        }
+
+        if (!$matches)
+            return;
+
+        if (in_array($browserRegex['name'], self::$browsers)) {
+            $short = array_search($browserRegex['name'], self::$browsers);
+        } else {
+            $short = 'XX';
+        }
+
+        $this->browser = array(
+            'name' => $this->buildBrowserName($browserRegex['name'], $matches),
+            'short_name' => $short,
+            'version' => $this->buildBrowserVersion($browserRegex['version'], $matches)
+        );
+    }
+
+    protected function parseMobile()
+    {
+        $mobileRegexes = $this->getMobileRegexes();
+        $this->parseBrand($mobileRegexes);
+        $this->parseModel($mobileRegexes);
+    }
+
+    protected function parseBrand($mobileRegexes)
+    {
+        foreach ($mobileRegexes as $brand => $mobileRegex) {
+            $matches = $this->matchUserAgent($mobileRegex['regex']);
+            if ($matches)
+                break;
+        }
+
+        if (!$matches)
+            return;
+        $this->brand = array_search($brand, self::$deviceBrands);
+        $this->fullName = $brand;
+
+        if (isset($mobileRegex['device'])) {
+            $this->device = array_search($mobileRegex['device'],self::$deviceTypes);
+        }
+
+        if (isset($mobileRegex['model'])) {
+            $this->model = $this->buildModel($mobileRegex['model'], $matches);
+        }
+    }
+
+    protected function parseModel($mobileRegexes)
+    {
+        if (empty($this->brand) || !empty($this->model))
+            return;
+
+        foreach ($mobileRegexes[$this->fullName]['models'] as $modelRegex) {
+            $matches = $this->matchUserAgent($modelRegex['regex']);
+            if ($matches)
+                break;
+        }
+
+        if (!$matches) {
+            return;
+        }
+
+        $this->model = $this->buildModel($modelRegex['model'], $matches);
+
+        if (isset($modelRegex['device'])) {
+            $this->device = array_search($modelRegex['device'], self::$deviceTypes);
+        }
+    }
+
+    protected function matchUserAgent($regex)
+    {
+        $regex = '/' . str_replace('/', '\/', $regex) . '/i';
+
+        if (preg_match($regex, $this->userAgent, $matches)) {
+            return $matches;
+        }
+
+        return false;
+    }
+
+    protected function buildOsName($osName, $matches)
+    {
+        return $this->buildByMatch($osName, $matches);
+    }
+
+    protected function buildOsVersion($osVersion, $matches)
+    {
+        $osVersion = $this->buildByMatch($osVersion, $matches);
+
+        $osVersion = $this->buildByMatch($osVersion, $matches, '2');
+
+        $osVersion = str_replace('_', '.', $osVersion);
+
+        return $osVersion;
+    }
+
+    protected function buildBrowserName($browserName, $matches)
+    {
+        return $this->buildByMatch($browserName, $matches);
+    }
+
+    protected function buildBrowserVersion($browserVersion, $matches)
+    {
+        $browserVersion = $this->buildByMatch($browserVersion, $matches);
+
+        $browserVersion = $this->buildByMatch($browserVersion, $matches, '2');
+
+        $browserVersion = str_replace('_', '.', $browserVersion);
+
+        return $browserVersion;
+    }
+
+    protected function buildModel($model, $matches)
+    {
+        $model = $this->buildByMatch($model, $matches);
+
+        $model = $this->buildByMatch($model, $matches, '2');
+
+        $model = $this->buildModelExceptions($model);
+
+        $model = str_replace('_', ' ', $model);
+
+        return $model;
+    }
+
+    protected function buildModelExceptions($model)
+    {
+        if ($this->brand == 'O2') {
+            $model = preg_replace('/([a-z])([A-Z])/', '$1 $2', $model);
+            $model = ucwords(str_replace('_', ' ', $model));
+        }
+
+        return $model;
+    }
+
+    /**
+     * This method is used in this class for processing results of pregmatch 
+     * results into string containing recognized information.
+     * 
+     * General algorithm:
+     * Parsing UserAgent string consists of trying to match it against list of
+     * regular expressions for three different information: 
+     * browser + version,
+     * OS + version, 
+     * device manufacturer + model. 
+     * 
+     * After match has been found iteration stops, and results are processed 
+     * by buildByMatch.
+     * As $item we get decoded name (name of browser, name of OS, name of manufacturer).
+     * In array $match we recieve preg_match results containing whole string matched at index 0
+     * and following matches in further indexes. Desired action now is to concatenate
+     * decoded name ($item) with matches found. First step is to append first found match,
+     * which is located in index=1 (that's why $nb is 1 by default).
+     * In other cases, where whe know that preg_match may return more than 1 result,
+     * we call buildByMatch with $nb = 2 or more, depending on what will be returned from
+     * regular expression.
+     * 
+     * Example:
+     * We are parsing UserAgent of Firefox 20.0 browser. 
+     * UserAgentParserEnhanced calls buildBrowserName() and buildBrowserVersion() in order
+     * to retrieve those information.
+     * In buildBrowserName() we only have one call of buildByMatch, where passed argument
+     * is regular expression testing given string for browser name. In this case, we are only
+     * interrested in first hit, so no $nb parameter will be set to 1. After finding match, and calling
+     * buildByMatch - we will receive just the name of browser.
+     * 
+     * Also after decoding browser we will get list of regular expressions for this browser name
+     * testing UserAgent string for version number. Again we iterate over this list, and after finding first
+     * occurence - we break loop and proceed to build by match. Since browser regular expressions can
+     * contain two hits (major version and minor version) in function buildBrowserVersion() we have
+     * two calls to buildByMatch, one without 3rd parameter, and second with $nb set to 2.
+     * This way we can retrieve version number, and assign it to object property.
+     * 
+     * In case of mobiles.yml this schema slightly varies, but general idea is the same.
+     * 
+     * @param string $item
+     * @param array $matches
+     * @param int $nb
+     * @return type
+     */
+    protected function buildByMatch($item, $matches, $nb = '1')
+    {
+        if (strpos($item, '$' . $nb) === false)
+            return $item;
+
+        $replace = isset($matches[$nb]) ? $matches[$nb] : '';
+        return trim(str_replace('$' . $nb, $replace, $item));
+    }
+
+    public function isBot()
+    {
+        $decodedFamily = '';
+        if (in_array($this->getOs('name'), self::$osShorts)) {
+            $osShort = self::$osShorts[$this->getOs('name')];
+        } else {
+            $osShort = '';
+        }
+        foreach (self::$osFamilies as $family => $familyOs) {
+            if (in_array($osShort, $familyOs)) {
+                $decodedFamily = $family;
+                break;
+            }
+        }
+
+        return $decodedFamily == 'Bot';
+    }
+
+    public function isSimulator()
+    {
+        $decodedFamily = '';
+        if (in_array($this->getOs('name'), self::$osShorts)) {
+            $osShort = self::$osShorts[$this->getOs('name')];
+        } else {
+            $osShort = '';
+        }
+        foreach (self::$osFamilies as $family => $familyOs) {
+            if (in_array($osShort, $familyOs)) {
+                $decodedFamily = $family;
+                break;
+            }
+        }
+        return $decodedFamily == 'Simulator';
+    }
+
+    public function isMobile()
+    {
+        return !$this->isDesktop();
+    }
+
+    public function isDesktop()
+    {
+        $osName = $this->getOs('name');
+        if (empty($osName) || empty(self::$osShorts[$osName])) {
+            return false;
+        }
+
+        $osShort = self::$osShorts[$osName];
+        foreach (self::$osFamilies as $family => $familyOs) {
+            if (in_array($osShort, $familyOs)) {
+                $decodedFamily = $family;
+                break;
+            }
+        }
+        return in_array($decodedFamily, self::$desktopOsArray);
+    }
+
+    public function getOs($attr = '')
+    {
+        if ($attr == '') {
+            return $this->os;
+        }
+
+        if (!isset($this->os[$attr])) {
+            return self::UNKNOWN;
+        }
+
+        if ($attr == 'version') {
+            $this->os['version'] = $this->os['version'];
+        }
+        return $this->os[$attr];
+    }
+
+    public function getBrowser($attr = '')
+    {
+        if ($attr == '') {
+            return $this->browser;
+        }
+
+        if (!isset($this->browser[$attr])) {
+            return self::UNKNOWN;
+        }
+
+        return $this->browser[$attr];
+    }
+
+    public function getDevice()
+    {
+        return $this->device;
+    }
+
+    public function getBrand()
+    {
+        return $this->brand;
+    }
+
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+    public function getUserAgent()
+    {
+        return $this->userAgent;
+    }
+
+    public static function getOsFamily($osLabel)
+    {
+        $osShortName = substr($osLabel, 0, 3);
+
+        foreach (self::$osFamilies as $osFamily => $osShortNames) {
+            if (in_array($osShortName, $osShortNames)) {
+                return $osFamily;
+            }
+        }
+
+        return 'Other';
+    }
+
+    public static function getBrowserFamily($browserLabel)
+    {
+        foreach (self::$browserFamilies as $browserFamily => $browserShortNames) {
+            if (in_array($browserLabel, $browserShortNames)) {
+                return $browserFamily;
+            }
+        }
+
+        return 'Other';
+    }
+
+    public static function getOsNameFromId($os, $ver = false)
+    {
+        $osFullName = array_search($os, self::$osShorts);
+        if ($osFullName) {
+            if (in_array($os, self::$osFamilies['Windows'])) {
+                return $osFullName;
+            } else {
+                return trim($osFullName . " " . $ver);
+            }
+        }
+        return false;
+    }
+
 }
\ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml
index c9f3a72e5a..0a4d2a6669 100644
--- a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml
@@ -1,408 +1,408 @@
-###############
-# Piwik - Open source web analytics
-#
-# @link http://piwik.org
-# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
-#
-# @category Piwik_Plugins
-# @package Piwik_DevicesDetection
-###############
-
-# SeaMonkey
-- regex: '(Iceape|SeaMonkey)/(\d+\.\d+)'
-  name: $1
-  version: '$2'
-  
-# Camino
-- regex: 'Camino/(\d+\.\d+)'
-  name: Camino
-  version: '$1'
-  
-#Fennec (Firefox for mobile)
-- regex: 'Fennec/(\d+\.\d+)'
-  name: Fennec
-  version: '$1'
-  
-#MicroB
-- regex: 'Firefox.*Tablet browser (\d+\.\d+)'
-  name: MicroB
-  version: '$1'
-  
-#Firefox
-- regex: 'Firefox/(\d+\.\d+)'
-  name: Firefox
-  version: '$1'
-- regex: '(BonEcho|GranParadiso|Lorentz|Minefield|Namoroka|Shiretoko)/(\d+\.\d+)'
-  name: Firefox '$1'
-  version: '$2'
-  
-#Flock
-- regex: 'Flock/(\d+\.\d+)'
-  name: Flock
-  version: '$1'
-  
-#RockMelt
-- regex: 'RockMelt/(\d+\.\d+)'
-  name: RockMelt
-  version: '$1'
-  
-#Netscape
-- regex: '(?:Navigator|Netscape6)/(\d+\.\d+)'
-  name: Netscape
-  version: '$1'
-  
-#Opera
-- regex: '(:?Opera Tablet.*Version|Opera/.+Opera Mobi.+Version|Safari.*OPR)/(\d+\.\d+)'
-  name: Opera Mobile
-  version: '$2'
-- regex: 'Opera Mini/(:?att/)?(\d+\.\d+)'
-  name: Opera Mini
-  version: '$2'
-- regex: 'Opera[/ ](?:9.80.*Version/)?(\d+\.\d+)'
-  name: Opera
-  version: '$1'
-  
-#wOSBrowser
-- regex: '(?:hpw|web)OS/(\d+\.\d+)'
-  name: wOSBrowser
-  version: '$1'
-  
-#Swiftfox
-- regex: 'Firefox/(\d+\.\d+).*\(Swiftfox\)'
-  name: Swiftfox
-  version: '$1'
-  
-#Rekonq
-- regex: 'rekonq'
-  name: Rekonq
-  version: ''
-  
-#Conkeror
-- regex: 'Conkeror/(\d+\.\d+)'
-  name: Conkeror
-  version: '$1'
-  
-#Konqueror
-- regex: 'Konqueror/(\d+\.\d+)'
-  name: Konqueror
-  version: '$1'
-  
-#Baidu Browser
-- regex: 'baidubrowser[/ ](\d+)'
-  name: Baidu Browser
-  version: '$1'
-  
-#Yandex Browser
-- regex: 'YaBrowser/(\d+)'
-  name: Yandex Browser
-  version: '$1'
-  
-#Chrome
-- regex: 'CrMo/(\d+\.\d+)'
-  name: Chrome Mobile
-  version: '$1'
-- regex: 'CriOS/(\d+\.\d+)'
-  name: Chrome Mobile iOS
-  version: '$1'
-- regex: 'Chrome/(\d+\.\d+).*Mobile'
-  name: Chrome Mobile
-  version: '$1'
-- regex: 'chromeframe/(\d+\.\d+)'
-  name: Chrome Frame
-  version: '$1'
-- regex: 'Chrome/(\d+\.\d+)'
-  name: Chrome
-  version: '$1'
-- regex: 'Chromium/(\d+\.\d+)'
-  name: Chromium
-  version: '$1'
-  
-#UC Browser
-- regex: 'UC[ ]?Browser[ /](\d+\.\d+)'
-  name: UC Browser
-  version: '$1'
-- regex: '(?:UC Browser|UCBrowser|UCWEB)(\d+\.\d+)'
-  name: UC Browser
-  version: '$1'
-  
-#Tizen Browser
-- regex: '(?:Tizen|SLP) Browser/(\d+\.\d+)'
-  name: Tizen Browser
-  version: '$1'
-  
-#Epiphany
-- regex: 'Epiphany/(\d+\.\d+)'
-  name: Epiphany
-  version: '$1'
-  
-#Fireweb Navigator
-- regex: 'Fireweb Navigator/(\d+\.\d+)'
-  name: Fireweb Navigator
-  version: '$1'
-  
-#Jasmine
-- regex: 'Jasmine[ /](\d+\.\d+)'
-  name: Jasmine
-  version: '$1'
-  
-#Lynx
-- regex: 'Lynx/(\d+\.\d+)'
-  name: Lynx
-  version: '$1'
-  
-#Midori
-- regex: 'Midori/(\d+\.\d+)'
-  name: Midori
-  version: '$1'
-  
-#NCSA Mosaic
-- regex: 'NCSA_Mosaic/(\d+\.\d+)'
-  name: NCSA Mosaic
-  version: '$1'
-  
-#ABrowse
-- regex: 'ABrowse (\d+\.\d+)'
-  name: ABrowse
-  version: '$1'
-  
-#Amaya
-- regex: 'amaya/(\d+\.\d+)'
-  name: Amaya
-  version: '$1'
-  
-#Amiga Voyager
-- regex: 'AmigaVoyager/(\d+\.\d+)'
-  name: Amiga Voyager
-  version: '$1'
-  
-#Amiga Aweb
-- regex: 'Amiga-Aweb/(\d+\.\d+)'
-  name: Amiga Aweb
-  version: '$1'
-  
-#Arora
-- regex: 'Arora/(\d+\.\d+)'
-  name: Arora
-  version: '$1'
-  
-#Beonex
-- regex: 'Beonex/(\d+\.\d+)'
-  name: Beonex
-  version: '$1'
-  
-#BlackBerry Browser
-- regex: 'Black[bB]erry|PlayBook|BB10'
-  name: BlackBerry Browser
-  version: ''
-  
-#BrowseX
-- regex: 'BrowseX \((\d+\.\d+)'
-  name: BrowseX
-  version: '$1'
-  
-#Cheshire
-- regex: 'Cheshire/(\d+\.\d+)'
-  name: Cheshire
-  version: '$1'
-  
-#CometBird
-- regex: 'CometBird/(\d+\.\d+)'
-  name: CometBird
-  version: '$1'
-  
-#Dillo
-- regex: 'Dillo/(\d+\.\d+)'
-  name: Dillo
-  version: '$1'
-  
-#Dolphin
-- regex: 'Dolfin/(\d+\.\d+)|dolphin'
-  name: Dolphin
-  version: '$1'
-  
-#Elinks
-- regex: 'Elinks/(\d+\.\d+)'
-  name: Elinks
-  version: '$1'
-  
-#Firebird
-- regex: 'Firebird/(\d+\.\d+)'
-  name: Firebird
-  version: '$1'
-  
-#Fluid
-- regex: 'Fluid/(\d+\.\d+)'
-  name: Fluid
-  version: '$1'
-  
-#Galeon
-- regex: 'Galeon/(\d+\.\d+)'
-  name: Galeon
-  version: '$1'
-  
-#Google Earth
-- regex: 'Google Earth/(\d+\.\d+)'
-  name: Google Earth
-  version: '$1'
-  
-#HotJava
-- regex: 'HotJava/(\d+\.\d+)'
-  name: HotJava
-  version: '$1'
-  
-#IBrowse
-- regex: 'IBrowse[ /](\d+\.\d+)'
-  name: IBrowse
-  version: '$1'
-  
-#iCab
-- regex: 'iCab[ /](\d+\.\d+)'
-  name: iCab
-  version: '$1'
-  
-#Internet Explorer
-- regex: 'IEMobile[ /](\d+\.\d+)'
-  name: IE Mobile
-  version: '$1'
-- regex: 'MSIE (\d+\.\d+).*XBLWP7'
-  name: IE Mobile
-  version: '$1'
-- regex: 'MSIE (\d+\.\d+)'
-  name: Internet Explorer
-  version: '$1'
-  
-#Iron
-- regex: 'Iron/(\d+\.\d+)'
-  name: Iron
-  version: '$1'
-  
-#Kapiko
-- regex: 'Kapiko/(\d+\.\d+)'
-  name: Kapiko
-  version: '$1'
-  
-#Kazehakase
-- regex: 'Kazehakase/(\d+\.\d+)'
-  name: Kazehakase
-  version: '$1'
-  
-#Kindle Browser
-- regex: 'Kindle/(\d+\.\d+)'
-  name: Kindle Browser
-  version: '$1'
-  
-#K-meleon
-- regex: 'K-meleon/(\d+\.\d+)'
-  name: K-meleon
-  version: '$1'
-  
-#Lightning
-- regex: 'Lightning/(\d+\.\d+)'
-  name: Lightning
-  version: '$1'
-  
-#Links
-- regex: 'Links \((\d+\.\d+)'
-  name: Links
-  version: '$1'
-  
-#Maxthon
-- regex: 'Maxthon (\d+\.\d+)'
-  name: Maxthon
-  version: '$1'
-- regex: '(?:Maxthon|MyIE2|Uzbl|Shiira)'
-  name: Maxthon
-  version: ''
-  
-#Openwave Mobile Browser
-- regex: 'UP.Browser/(\d+\.\d+)'
-  name: Openwave Mobile Browser
-  version: '$1'
-  
-#OmniWeb
-- regex: 'OmniWeb/[v]?(\d+\.\d+)'
-  name: OmniWeb
-  version: '$1'
-  
-#Phoenix
-- regex: 'Phoenix/(\d+\.\d+)'
-  name: Phoenix
-  version: '$1'
-  
-#Mobile Silk
-- regex: 'Silk/(\d+\.\d+)'
-  name: Mobile Silk
-  version: '$1'
-  
-#Nokia Browser
-- regex: '(?:NokiaBrowser|BrowserNG)/(\d+\.\d+)'
-  name: Nokia Browser
-  version: '$1'
-- regex: 'Series60/5\.0'
-  name: Nokia Browser
-  version: '7.0'
-- regex: 'Series60/(\d+\.\d+)'
-  name: Nokia OSS Browser
-  version: '$1'
-- regex: 'S40OviBrowser/(\d+\.\d+)'
-  name: Nokia Ovi Browser
-  version: '$1'
-- regex: '^Nokia|Nokia[EN]?\d+'
-  name: Nokia Browser
-  version: ''
-  
-#NetFront
-- regex: 'NetFrontLifeBrowser/(\d+\.\d+)'
-  name: NetFront Life
-  version: '$1'
-- regex: 'NetFront/(\d+\.\d+)'
-  name: NetFront
-  version: '$1'
-- regex: 'PLAYSTATION|NINTENDO 3|AppleWebKit.+ NX/\d+\.\d+\.\d+'
-  name: NetFront
-  version: ''
-  
-#Obigo
-- regex: 'Obigo[ ]?(?:InternetBrowser|Browser)?[ /]([A-Za-z0-9]*)'
-  name: Obigo
-  version: '$1'
-- regex: 'Obigo|Teleca'
-  name: Obigo
-  version: ''
-  
-#Palm Blazer
-- regex: 'Blazer/(\d+\.\d+)'
-  name: Palm Blazer
-  version: '$1'
-- regex: 'Pre/(\d+\.\d+)'
-  name: Palm Pre
-  version: '$1'
-  
-#Polaris
-- regex: '(?:Polaris|Embider)/(\d+\.\d+)'
-  name: Polaris
-  version: '$1'
-  
-#Snowshoe
-- regex: 'Snowshoe/(\d+\.\d+)'
-  name: Snowshoe
-  version: '$1'
-  
-#Safari
-- regex: '(?:iPod|iPad|iPhone).+Version/(\d+\.\d+)'
-  name: Mobile Safari
-  version: '$1'
-- regex: 'Version/(\d+\.\d+).*Mobile.*Safari/'
-  name: Mobile Safari
-  version: '$1'
-- regex: '(?:iPod|iPhone|iPad)'
-  name: Mobile Safari
-  version: ''
-- regex: 'Version/(\d+\.\d+).*Safari/|Safari/\d+'
-  name: Safari
-  version: '$1'
-  
-#Android Browser
-- regex: 'Android'
-  name: Android Browser
+###############
+# Piwik - Open source web analytics
+#
+# @link http://piwik.org
+# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+#
+# @category Piwik_Plugins
+# @package Piwik_DevicesDetection
+###############
+
+# SeaMonkey
+- regex: '(Iceape|SeaMonkey)/(\d+\.\d+)'
+  name: $1
+  version: '$2'
+  
+# Camino
+- regex: 'Camino/(\d+\.\d+)'
+  name: Camino
+  version: '$1'
+  
+#Fennec (Firefox for mobile)
+- regex: 'Fennec/(\d+\.\d+)'
+  name: Fennec
+  version: '$1'
+  
+#MicroB
+- regex: 'Firefox.*Tablet browser (\d+\.\d+)'
+  name: MicroB
+  version: '$1'
+  
+#Firefox
+- regex: 'Firefox/(\d+\.\d+)'
+  name: Firefox
+  version: '$1'
+- regex: '(BonEcho|GranParadiso|Lorentz|Minefield|Namoroka|Shiretoko)/(\d+\.\d+)'
+  name: Firefox '$1'
+  version: '$2'
+  
+#Flock
+- regex: 'Flock/(\d+\.\d+)'
+  name: Flock
+  version: '$1'
+  
+#RockMelt
+- regex: 'RockMelt/(\d+\.\d+)'
+  name: RockMelt
+  version: '$1'
+  
+#Netscape
+- regex: '(?:Navigator|Netscape6)/(\d+\.\d+)'
+  name: Netscape
+  version: '$1'
+  
+#Opera
+- regex: '(:?Opera Tablet.*Version|Opera/.+Opera Mobi.+Version|Safari.*OPR)/(\d+\.\d+)'
+  name: Opera Mobile
+  version: '$2'
+- regex: 'Opera Mini/(:?att/)?(\d+\.\d+)'
+  name: Opera Mini
+  version: '$2'
+- regex: 'Opera[/ ](?:9.80.*Version/)?(\d+\.\d+)'
+  name: Opera
+  version: '$1'
+  
+#wOSBrowser
+- regex: '(?:hpw|web)OS/(\d+\.\d+)'
+  name: wOSBrowser
+  version: '$1'
+  
+#Swiftfox
+- regex: 'Firefox/(\d+\.\d+).*\(Swiftfox\)'
+  name: Swiftfox
+  version: '$1'
+  
+#Rekonq
+- regex: 'rekonq'
+  name: Rekonq
+  version: ''
+  
+#Conkeror
+- regex: 'Conkeror/(\d+\.\d+)'
+  name: Conkeror
+  version: '$1'
+  
+#Konqueror
+- regex: 'Konqueror/(\d+\.\d+)'
+  name: Konqueror
+  version: '$1'
+  
+#Baidu Browser
+- regex: 'baidubrowser[/ ](\d+)'
+  name: Baidu Browser
+  version: '$1'
+  
+#Yandex Browser
+- regex: 'YaBrowser/(\d+)'
+  name: Yandex Browser
+  version: '$1'
+  
+#Chrome
+- regex: 'CrMo/(\d+\.\d+)'
+  name: Chrome Mobile
+  version: '$1'
+- regex: 'CriOS/(\d+\.\d+)'
+  name: Chrome Mobile iOS
+  version: '$1'
+- regex: 'Chrome/(\d+\.\d+).*Mobile'
+  name: Chrome Mobile
+  version: '$1'
+- regex: 'chromeframe/(\d+\.\d+)'
+  name: Chrome Frame
+  version: '$1'
+- regex: 'Chrome/(\d+\.\d+)'
+  name: Chrome
+  version: '$1'
+- regex: 'Chromium/(\d+\.\d+)'
+  name: Chromium
+  version: '$1'
+  
+#UC Browser
+- regex: 'UC[ ]?Browser[ /](\d+\.\d+)'
+  name: UC Browser
+  version: '$1'
+- regex: '(?:UC Browser|UCBrowser|UCWEB)(\d+\.\d+)'
+  name: UC Browser
+  version: '$1'
+  
+#Tizen Browser
+- regex: '(?:Tizen|SLP) Browser/(\d+\.\d+)'
+  name: Tizen Browser
+  version: '$1'
+  
+#Epiphany
+- regex: 'Epiphany/(\d+\.\d+)'
+  name: Epiphany
+  version: '$1'
+  
+#Fireweb Navigator
+- regex: 'Fireweb Navigator/(\d+\.\d+)'
+  name: Fireweb Navigator
+  version: '$1'
+  
+#Jasmine
+- regex: 'Jasmine[ /](\d+\.\d+)'
+  name: Jasmine
+  version: '$1'
+  
+#Lynx
+- regex: 'Lynx/(\d+\.\d+)'
+  name: Lynx
+  version: '$1'
+  
+#Midori
+- regex: 'Midori/(\d+\.\d+)'
+  name: Midori
+  version: '$1'
+  
+#NCSA Mosaic
+- regex: 'NCSA_Mosaic/(\d+\.\d+)'
+  name: NCSA Mosaic
+  version: '$1'
+  
+#ABrowse
+- regex: 'ABrowse (\d+\.\d+)'
+  name: ABrowse
+  version: '$1'
+  
+#Amaya
+- regex: 'amaya/(\d+\.\d+)'
+  name: Amaya
+  version: '$1'
+  
+#Amiga Voyager
+- regex: 'AmigaVoyager/(\d+\.\d+)'
+  name: Amiga Voyager
+  version: '$1'
+  
+#Amiga Aweb
+- regex: 'Amiga-Aweb/(\d+\.\d+)'
+  name: Amiga Aweb
+  version: '$1'
+  
+#Arora
+- regex: 'Arora/(\d+\.\d+)'
+  name: Arora
+  version: '$1'
+  
+#Beonex
+- regex: 'Beonex/(\d+\.\d+)'
+  name: Beonex
+  version: '$1'
+  
+#BlackBerry Browser
+- regex: 'Black[bB]erry|PlayBook|BB10'
+  name: BlackBerry Browser
+  version: ''
+  
+#BrowseX
+- regex: 'BrowseX \((\d+\.\d+)'
+  name: BrowseX
+  version: '$1'
+  
+#Cheshire
+- regex: 'Cheshire/(\d+\.\d+)'
+  name: Cheshire
+  version: '$1'
+  
+#CometBird
+- regex: 'CometBird/(\d+\.\d+)'
+  name: CometBird
+  version: '$1'
+  
+#Dillo
+- regex: 'Dillo/(\d+\.\d+)'
+  name: Dillo
+  version: '$1'
+  
+#Dolphin
+- regex: 'Dolfin/(\d+\.\d+)|dolphin'
+  name: Dolphin
+  version: '$1'
+  
+#Elinks
+- regex: 'Elinks/(\d+\.\d+)'
+  name: Elinks
+  version: '$1'
+  
+#Firebird
+- regex: 'Firebird/(\d+\.\d+)'
+  name: Firebird
+  version: '$1'
+  
+#Fluid
+- regex: 'Fluid/(\d+\.\d+)'
+  name: Fluid
+  version: '$1'
+  
+#Galeon
+- regex: 'Galeon/(\d+\.\d+)'
+  name: Galeon
+  version: '$1'
+  
+#Google Earth
+- regex: 'Google Earth/(\d+\.\d+)'
+  name: Google Earth
+  version: '$1'
+  
+#HotJava
+- regex: 'HotJava/(\d+\.\d+)'
+  name: HotJava
+  version: '$1'
+  
+#IBrowse
+- regex: 'IBrowse[ /](\d+\.\d+)'
+  name: IBrowse
+  version: '$1'
+  
+#iCab
+- regex: 'iCab[ /](\d+\.\d+)'
+  name: iCab
+  version: '$1'
+  
+#Internet Explorer
+- regex: 'IEMobile[ /](\d+\.\d+)'
+  name: IE Mobile
+  version: '$1'
+- regex: 'MSIE (\d+\.\d+).*XBLWP7'
+  name: IE Mobile
+  version: '$1'
+- regex: 'MSIE (\d+\.\d+)'
+  name: Internet Explorer
+  version: '$1'
+  
+#Iron
+- regex: 'Iron/(\d+\.\d+)'
+  name: Iron
+  version: '$1'
+  
+#Kapiko
+- regex: 'Kapiko/(\d+\.\d+)'
+  name: Kapiko
+  version: '$1'
+  
+#Kazehakase
+- regex: 'Kazehakase/(\d+\.\d+)'
+  name: Kazehakase
+  version: '$1'
+  
+#Kindle Browser
+- regex: 'Kindle/(\d+\.\d+)'
+  name: Kindle Browser
+  version: '$1'
+  
+#K-meleon
+- regex: 'K-meleon/(\d+\.\d+)'
+  name: K-meleon
+  version: '$1'
+  
+#Lightning
+- regex: 'Lightning/(\d+\.\d+)'
+  name: Lightning
+  version: '$1'
+  
+#Links
+- regex: 'Links \((\d+\.\d+)'
+  name: Links
+  version: '$1'
+  
+#Maxthon
+- regex: 'Maxthon (\d+\.\d+)'
+  name: Maxthon
+  version: '$1'
+- regex: '(?:Maxthon|MyIE2|Uzbl|Shiira)'
+  name: Maxthon
+  version: ''
+  
+#Openwave Mobile Browser
+- regex: 'UP.Browser/(\d+\.\d+)'
+  name: Openwave Mobile Browser
+  version: '$1'
+  
+#OmniWeb
+- regex: 'OmniWeb/[v]?(\d+\.\d+)'
+  name: OmniWeb
+  version: '$1'
+  
+#Phoenix
+- regex: 'Phoenix/(\d+\.\d+)'
+  name: Phoenix
+  version: '$1'
+  
+#Mobile Silk
+- regex: 'Silk/(\d+\.\d+)'
+  name: Mobile Silk
+  version: '$1'
+  
+#Nokia Browser
+- regex: '(?:NokiaBrowser|BrowserNG)/(\d+\.\d+)'
+  name: Nokia Browser
+  version: '$1'
+- regex: 'Series60/5\.0'
+  name: Nokia Browser
+  version: '7.0'
+- regex: 'Series60/(\d+\.\d+)'
+  name: Nokia OSS Browser
+  version: '$1'
+- regex: 'S40OviBrowser/(\d+\.\d+)'
+  name: Nokia Ovi Browser
+  version: '$1'
+- regex: '^Nokia|Nokia[EN]?\d+'
+  name: Nokia Browser
+  version: ''
+  
+#NetFront
+- regex: 'NetFrontLifeBrowser/(\d+\.\d+)'
+  name: NetFront Life
+  version: '$1'
+- regex: 'NetFront/(\d+\.\d+)'
+  name: NetFront
+  version: '$1'
+- regex: 'PLAYSTATION|NINTENDO 3|AppleWebKit.+ NX/\d+\.\d+\.\d+'
+  name: NetFront
+  version: ''
+  
+#Obigo
+- regex: 'Obigo[ ]?(?:InternetBrowser|Browser)?[ /]([A-Za-z0-9]*)'
+  name: Obigo
+  version: '$1'
+- regex: 'Obigo|Teleca'
+  name: Obigo
+  version: ''
+  
+#Palm Blazer
+- regex: 'Blazer/(\d+\.\d+)'
+  name: Palm Blazer
+  version: '$1'
+- regex: 'Pre/(\d+\.\d+)'
+  name: Palm Pre
+  version: '$1'
+  
+#Polaris
+- regex: '(?:Polaris|Embider)/(\d+\.\d+)'
+  name: Polaris
+  version: '$1'
+  
+#Snowshoe
+- regex: 'Snowshoe/(\d+\.\d+)'
+  name: Snowshoe
+  version: '$1'
+  
+#Safari
+- regex: '(?:iPod|iPad|iPhone).+Version/(\d+\.\d+)'
+  name: Mobile Safari
+  version: '$1'
+- regex: 'Version/(\d+\.\d+).*Mobile.*Safari/'
+  name: Mobile Safari
+  version: '$1'
+- regex: '(?:iPod|iPhone|iPad)'
+  name: Mobile Safari
+  version: ''
+- regex: 'Version/(\d+\.\d+).*Safari/|Safari/\d+'
+  name: Safari
+  version: '$1'
+  
+#Android Browser
+- regex: 'Android'
+  name: Android Browser
   version: ''
\ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml
index 64b1470ef2..d9a2a22f88 100644
--- a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml
@@ -1,958 +1,958 @@
-###############
-# Piwik - Open source web analytics
-#
-# @link http://piwik.org
-# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
-#
-# @category Piwik_Plugins
-# @package Piwik_DevicesDetection
-###############
-
-# HTC
-HTC:
-  regex: 'HTC|Sprint APA|ADR[A-Za-z0-9]+'
-  device: 'smartphone'
-  models:
-    - regex: 'HTC ([A-Za-z0-9]+) Build'
-      model: '$1'
-    - regex: 'HTC ([A-Za-z0-9]+(?: [A-Za-z0-9]+)?)'
-      model: '$1'
-    - regex: 'USCCHTC(\d+)'
-      model: '$1'
-    - regex: 'Sprint APA(9292)'
-      model: '$1 (Sprint)'
-    - regex: 'HTC_([A-Za-z0-9_]+)'
-      model: '$1'
-    - regex: 'HTC(?:[\-/ ])?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'HTC;(?: )?([A-Za-z0-9 ]+)'
-      model: '$1'
-    - regex: '(ADR[A-Za-z0-9]+)'
-      model: '$1'
-
-# Tesla Model S
-Tesla:
-  regex: 'QtCarBrowser'
-  device: 'car browser'
-  model: 'Model S'
-
-# Kindle
-Kindle:
-  regex: 'KF(?:OT|TT|JWI|JWA) Build|Kindle|Silk/(\d+)\.(\d+)'
-  device: 'tablet'
-  models:
-    - regex: 'KFOT|Kindle Fire|Silk/(\d+)\.(\d+)'
-      model: 'Fire'
-    - regex: 'KFTT'
-      model: 'Fire HD'
-    - regex: 'KFJWI'
-      model: 'Fire HD 8.9" WiFi'
-    - regex: 'KFJWA'
-      model: 'Fire HD 8.9" 4G'
-
-# NOKIA
-Nokia:
-  regex: 'Nokia|Lumia|Maemo RX|portalmmm/2\.0 N7|portalmmm/2\.0 NK|nok[0-9]+|Symbian.*\s([a-zA-Z0-9]+)$'
-  device: 'smartphone'
-  models:
-    - regex: 'NokiaInternal|Nokia-WAP-Toolkit|Nokia-MIT-Browser|Nokia Mobile|Nokia Browser|Nokia/Series'
-      model: ''
-    - regex: 'Nokia(N[0-9]+)'
-      model: '$1'
-    - regex: 'Nokia-([A-Za-z0-9]+)'
-      model: 'N$1'
-    - regex: 'NOKIA; ([A-Za-z0-9\- ]+)'
-      model: '$1'
-    - regex: 'NOKIA[ ]?([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'NOKIA/([A-Za-z0-9 ]+)'
-      model: '$1'
-    - regex: '(Lumia [A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'Maemo RX-51 ([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'Maemo RX-34'
-      model: 'N800'
-    - regex: 'portalmmm/2\.0 (N7[37]|NK[A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'nok([0-9]+)'
-      model: '$1'
-    - regex: 'Symbian.*\s([a-zA-Z0-9]+)$'
-      device: 'feature phone'
-      model: '$1'
-  
-# RIM/BlackBerry
-RIM:
-  regex: 'BB10;|BlackBerry|rim[0-9]+|PlayBook'
-  device: 'smartphone'
-   
-  models:
-    - regex: 'BB10; ([A-Za-z0-9\- ]+)\)'
-      model: 'BlackBerry $1'
-    - regex: 'PlayBook.+RIM Tablet OS'
-      model: 'BlackBerry Playbook'
-      device: 'tablet'
-    - regex: 'BlackBerry(?: )?([A-Za-z0-9]+)'
-      model: 'BlackBerry $1'
-    - regex: 'rim([0-9]+)'
-      model: 'BlackBerry $1'
-    - regex: 'BlackBerry'
-      model: 'BlackBerry'
-
-# PALM
-Palm:
-  regex: '(?:Pre|Pixi)/(\d+)\.(\d+)|Palm|Treo'
-  device: 'smartphone'
-  models:
-    - regex: '((?:Pre|Pixi))/(\d+\.\d+)'
-      model: '$1 $2'
-    - regex: 'Palm(?: )?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'Treo([A-Za-z0-9]+)'
-      model: 'Treo $1'
-
-# HP
-HP:
-  regex: 'Touch[Pp]ad|hp-tablet|HP(?: )?iPAQ|webOS.*(P160U)'
-  device: 'smartphone'
-  models:
-    - regex: 'Touch[Pp]ad/(\d+\.\d+)|hp-tablet'
-      model: 'TouchPad'
-      device: 'tablet'
-    - regex: 'HP(?: )?iPAQ(?: )?([A-Za-z0-9]+)'
-      model: 'iPAQ $1'
-    - regex: 'webOS.*(P160U)'
-      model: 'Veer'
-
-# TiPhone
-TiPhone:
-  regex: 'TiPhone(?: )?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Apple
-Apple:
-  regex: 'AppleTV|iPad|iPod|iPhone'
-  models:
-    - regex: 'AppleTV'
-      model: 'Apple TV'
-      device: 'tv'
-    - regex: 'iPad'
-      model: 'iPad'
-      device: 'tablet'
-    - regex: 'iPod'
-      model: 'iPod Touch'
-      device: 'palmtop'
-    - regex: 'iPhone'
-      model: 'iPhone'
-      device: 'smartphone'
-
-# Acer
-Acer:
-  regex: 'acer[\-_]([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-   
-# Airness
-Airness:
-  regex: 'AIRNESS-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-   
-# Alcatel
-Alcatel:
-  regex: 'Alcatel|Alc([A-Za-z0-9]+)'
-  device: 'smartphone'
-  models:
-    - regex: 'Alcatel UP'
-      model: ''
-    - regex: 'ALCATEL[ \-]([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'ALCATEL_([A-Za-z0-9_]+)'
-      model: '$1'
-    - regex: 'Alc([A-Za-z0-9]+)'
-      model: '$1'
-
-# Amoi
-Amoi:
-  regex: 'Amoi'
-  device: 'smartphone'
-  models:
-    - regex: 'Amoi[\- /](A-Za-z0-9]+)'
-      mobile: '$1'
-    - regex: 'Amoisonic-([A-Za-z0-9]+)'
-      model: '$1'
-
-# Asus
-Asus:
-  regex: 'Asus'
-  device: 'smartphone'
-  models:
-    - regex: 'Asus(?:-|;)?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'ASUS (Transformer Pad TF300T)'
-      device: 'tablet'
-      model: '$1'
-
-# Audiovox
-Audiovox:
-  regex: 'Audiovox|CDM|UTS(?:TARCOM)?\-|audio([A-Za-z0-9\-]+)'
-  device: 'smartphone'
-  models:
-    - regex: 'Audiovox[_\-]([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'CDM(?:-)?([A-Za-z0-9]+)'
-      model: 'CDM-$1'
-    - regex: 'UTS(?:TARCOM)?-([A-Za-z0-9\-]+)'
-      model: 'CDM-$1'
-    - regex: 'audio([A-Za-z0-9\-]+)'
-      model: 'CDM-$1'
-
-# Avvio
-Avvio:
-  regex: 'Avvio[ _]([A-Za-z0-9\-]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Bird
-Bird:
-  regex: 'BIRD[\-. _]([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Becker
-Becker:
-  regex: 'Becker-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Beetel
-Beetel:
-  regex: 'Beetel ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-  
-# BenQ-Siemens
-BenQ-Siemens:
-  regex: 'BENQ-SIEMENS - ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-  
-# BenQ
-BenQ:
-  regex: 'BENQ(?:[ \-])?([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Capitel
-Capitel:
-  regex: 'Capitel-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Compal
-Compal:
-  regex: 'Compal-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Cricket
-Cricket:
-  regex: 'Cricket-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Dell
-Dell:
-  regex: 'Dell ([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Dbtel
-Dbtel:
-  regex: 'DBTEL(?:[\-/])?([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Dicam
-Dicam:
-  regex: 'DICAM-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# DoCoMo
-DoCoMo:
-  regex: 'DoCoMo|\;FOMA|KGT/1\.0'
-  device: 'feature phone'
-  models:
-    - regex: 'DoCoMo/[12]\.0[/ ]([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '([A-Za-z0-9]+)(?:_W)?\;FOMA'
-      model: '$1'
-    - regex: 'KGT/1\.0 ([A-Za-z0-9]+)'
-      model: '$1'
-
-# Dopod
-Dopod:
-  regex: 'Dopod(?: )?([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Ericy
-Ericy:
-  regex: 'Ericy-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Sony Ericsson
-Sony Ericsson:
-  regex: 'Sony(?: )?Ericsson|portalmmm/2\.0 K'
-  device: 'smartphone'
-  models:
-    - regex: 'SonyEricsson([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'Sony(?: )?Ericsson ([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'portalmmm/2.0 K([A-Za-z0-9]+)'
-      model: 'K$1'
-
-# Ericsson
-Ericsson:
-  regex: 'Ericsson(?:/ )?([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# eTouch
-eTouch:
-  regex: 'eTouch(?: )?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Ezze
-Ezze:
-  regex: 'EZZE-|EZ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  models: 
-    - regex: 'EZZE-([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'EZ([A-Za-z0-9]+)'
-      model: 'EZ$1'
-
-# Ezio
-Ezio:
-  regex: 'EZIO-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Gionee
-Gionee:
-  regex: 'GIONEE-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Google
-Google:
-  regex: 'Nexus|GoogleTV'
-  device: 'smartphone'
-  models:
-    - regex: '(Galaxy Nexus)'
-      model: '$1'
-    - regex: '(Nexus (:?S|4|One))'
-      model: '$1'
-    - regex: '(Nexus (:?7|10))'
-      device: 'tablet'
-      model: '$1'
-    - regex: '(GoogleTV)'
-      device: 'tv'
-      model: '$1'
-
-# Gradiente
-Gradiente:
-  regex: 'GRADIENTE'
-  device: 'feature phone'
-  models: 
-    - regex: 'GRADIENTE-([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'GRADIENTE ([A-Za-z0-9\-]+)'
-      model: '$1'
-
-# Grundig
-Grundig:
-  regex: 'GRUNDIG|portalmmm/2\.0 G'
-  device: 'tv'
-  models: 
-    - regex: 'GRUNDIG ([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'portalmmm/2\.0 G([A-Za-z0-9]+)'
-      model: 'G$1'
-
-# Haier
-Haier:
-  regex: 'Haier[ -]([A-Za-z0-9\-]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Huawei
-Huawei:
-  regex: 'Huawei|vodafone([A-Za-z0-9]+)'
-  device: 'smartphone'
-  models:
-    - regex: 'Huawei(?:[\- /_]|/1\.0/)?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'vodafone([A-Za-z0-9]+)'
-      model: 'Vodafone $1'
-
-# Innostream
-Innostream:
-  regex: 'INNO([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: 'INNO$1'
-
-# Inq
-INQ:
-  regex: 'INQ/([A-Za-z0-9\-]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# i-mate
-i-mate:
-  regex: 'i-mate ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# i-mobile
-i-mobile:
-  regex: 'i-mobile(?: )?([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# ikomo
-iKoMo:
-  regex: 'iKoMo ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# kddi
-KDDI:
-  regex: 'kddi-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# kyocera
-Kyocera:
-  regex: 'Kyocera|KWC-|QC-'
-  device: 'smartphone'
-  models:
-    - regex: 'Kyocera-KZ-([A-Za-z0-9]+)'
-      model: 'KZ $1'
-    - regex: 'Kyocera(:?[\-/])?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '(?:KWC|QC)-([A-Za-z0-9]+)'
-      model: '$1'
-
-# lanix
-Lanix:
-  regex: 'LANIX-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# lct
-LCT:
-  regex: 'LCT_([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# lenovo
-Lenovo:
-  regex: 'Lenovo[\-_]([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# lguplus
-LGUPlus:
-  regex: 'LGUPlus'
-  device: 'smartphone'
-  model: ''
-
-# lg
-LG:
-  regex: 'LG|portalmmm/2\.0 (?:KE|KG|KP|L3)|VX[0-9]+'
-  device: 'smartphone'
-  models:
-    - regex: 'LGE_DLNA_SDK'
-      device: 'tv'
-      model: 'NetCast'
-    - regex: 'LGE(?: |-LG| LG-AX|-)([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'LGE;([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'LG(?:/|-LG| |-)?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'LG; ([A-Za-z0-9 ]+)'
-      model: '$1'
-    - regex: 'portalmmm/2.0 ((?:KE|KG|KP|L3)[A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '(VX[0-9]+)'
-      model: '$1'
-
-# microsoft
-Microsoft:
-  regex: 'Xbox|KIN\.(?:One|Two)'
-  device: 'console'
-  model: 'Xbox 360'
-
-# Konka
-Konka:
-  regex: 'KONKA_([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Karbonn
-Karbonn:
-  regex: 'Karbonn_([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Sagem
-Sagem:
-  regex: 'SAGEM|portalmmm/2.0 (?:SG|my)'
-  device: 'smartphone'
-  models:
-    - regex: 'SAGEM ([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'SAGEM-([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'portalmmm/2.0 ((?:SG|my)[A-Za-z0-9]+)'
-      model: '$1'
-
-# micromax
-MicroMax:
-  regex: 'MicroMax(?:[ \-])?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# mio
-Mio:
-  regex: 'MIO(?:/)?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# mitsubishi
-Mitsubishi:
-  regex: 'MITSU|portalmmm/[12]\.0 M'
-  device: 'feature phone'
-  models:
-    - regex: 'MITSU/[A-Za-z0-9.]+ \(([A-Za-z0-9]+)\)'
-      model: '$1'
-    - regex: 'MITSU[ \-]?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'portalmmm/[12]\.0 (M[A-Za-z0-9]+)'
-      model: '$1'
-
-# motorola
-Motorola:
-  regex: 'MOT|(?<!AN)DROID (?:Build|([A-Za-z0-9]+))|portalmmm/2.0 (?:E378i|L6|L7|v3)'
-  device: 'smartphone'
-  models:
-    - regex: 'Motorola[ \-]([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'MOTORAZR[ \-]([A-Za-z0-9]+)'
-      model: 'RAZR $1'
-    - regex: 'MOTORIZR[ \-]([A-Za-z0-9]+)'
-      model: 'RIZR $1'
-    - regex: 'MOT[O]?[\-]?([A-Za-z0-9.]+)'
-      model: '$1'
-    - regex: '(?<!AN)DROID (?:Build|([A-Za-z0-9]+))'
-      model: 'DROID $1'
-    - regex: 'portalmmm/2.0 ((?:E378i|L6|L7|V3)[A-Za-z0-9]+)'
-      model: '$1'
-
-# myphone
-MyPhone:
-  regex: 'MyPhone([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# nec
-NEC:
-  regex: 'NEC|KGT/2\.0|portalmmm/1\.0 (?:DB|N)|(?:portalmmm|o2imode)/2.0[ ,]N'
-  device: 'smartphone'
-  models:
-    - regex: '(?:NEC-|KGT/2\.0 )([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'portalmmm/1\.0 ((?:DB|N)[A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '(?:portalmmm|o2imode)/2\.0[ ,](N[A-Za-z0-9]+)'
-      model: '$1'
-
-# newgen
-Newgen:
-  regex: 'NEWGEN\-([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-  
-# nintendo
-Nintendo:
-  regex: 'Nintendo (([3]?DS[i]?)|Wii[U]?)'
-  device: 'console'
-  model: '$1'
-  
-# ngm
-NGM:
-  regex: 'NGM_([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-  
-# nexian
-Nexian:
-  regex: 'Nexian'
-  device: 'smartphone'
-  models:
-    - regex: 'Nexian[ ]?([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'Nexian-([A-Za-z0-9]+)'
-      model: '$1'
-  
-# o2
-O2:
-  regex: 'Xda|O2[ \-]|COCOON'
-  device: 'smartphone'
-  models:
-    - regex: '(Xda[ _][A-Za-z0-9_]+)'
-      models: '$1'
-    - regex: '(COCOON)'
-      models: '$1'
-    - regex: 'O2 ([A-Za-z0-9 ]+)'
-      models: '$1'
-    - regex: 'O2-([A-Za-z0-9]+)'
-      models: '$1'
-  
-# onda
-Onda:
-  regex: 'Onda'
-  device: 'smartphone'
-  models:
-    regex: '([A-Za-z0-9]+)[ _]Onda'
-    model: '$1'
-    regex: 'Onda ([A-Za-z0-9]+)'
-    model: '$1'
-  
-# oppo
-OPPO:
-  regex: 'OPPO[ ]?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-  
-# orange
-Orange:
-  regex: 'SPV[ \-]?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: 'SPV $1'
-
-# panasonic
-Panasonic:
-  regex: 'Panasonic'
-  device: 'smartphone'
-  models:
-    - regex: 'Panasonic MIL DLNA'
-      device: 'tv'
-      model: 'Viera Cast'
-    - regex: 'Panasonic[ \-]?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'portalmmm/2.0 (P[A-Za-z0-9]+)'
-      model: '$1'
- 
-# philips
-Philips:
-  regex: 'Philips'
-  device: 'smartphone'
-  models:
-    - regex: 'Philips-FISIO ([A-Za-z0-9]+)'
-      model: 'Fisio $1'
-    - regex: 'Philips[ ]?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'Philips-([A-Za-z0-9\-@]+)'
-      model: '$1'
- 
-# phoneOne
-phoneOne:
-  regex: 'phoneOne[ \-]?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
- 
-# Rover
-Rover:
-  regex: 'Rover ([0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Siemens
-Siemens:
-  regex: 'SIEMENS|SIE-|portalmmm/2\.0 SI|S55|SL45i'
-  device: 'smartphone'
-  models:
-    - regex: 'SIEMENS[ \-]([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'SIE(?:MENS )?[\-]?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '(S55|SL45i)'
-      model: '$1'
-    - regex: 'portalmmm/2.0 (SI[A-Za-z0-9]+)'
-      model: '$1'
-
-# Samsung
-Samsung:
-  regex: 'SAMSUNG|S(?:CH|GH|PH|EC|AM)-|SMART-TV|GT-|Galaxy|(?:portalmmm|o2imode)/2\.0 [SZ]|sam[rua]'
-  device: 'smartphone'
-  models:
-    - regex: 'SAMSUNG[\-;][ ]?([A-Za-z0-9]+[\-_][A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'SAMSUNG[ _/]?([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'SAMSUNG;[ ]?([A-Za-z0-9 ]+)'
-      model: '$1'
-    - regex: '((?:SCH|SGH|SPH|GT)-[A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'SEC-([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'SAM-([A-Za-z0-9]+)'
-      model: 'SCH-$1'
-    - regex: 'SMART-TV'
-      device: 'tv'
-      model: 'Smart TV'
-    - regex: '(Galaxy [A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '(?:portalmmm|o2imode)/2\.0 ([SZ][A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'sam([rua][0-9]+)'
-      model: 'SCH-$1'
-
-# pantech
-Pantech:
-  regex: 'Pantech|P[GTN]-|TX[T]?[0-9]+'
-  device: 'smartphone'
-  models:
-    - regex: 'Pantech[ \-]?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'Pantech_([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: '(P[GTN]-[A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '(TX[T]?[0-9]+)'
-      model: '$1'  
-
-# Sanyo
-Sanyo:
-  regex: 'Sanyo|MobilePhone '
-  device: 'smartphone'
-  models:
-    - regex: 'SANYO[ \-_]([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'MobilePhone ([A-Za-z0-9\-]+)'
-      model: '$1'
-
-# Sega
-Sega:
-  regex: 'Dreamcast'
-  device: 'console'
-  model: 'Dreamcast'
-
-# Sendo
-Sendo:
-  regex: 'Sendo([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Spice
-Spice:
-  regex: 'Spice'
-  device: 'smartphone'
-  models: 
-    - regex: 'Spice ([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'Spice-([A-Za-z0-9]+)'
-      model: '$1'
-  
-# Sharp
-Sharp:
-  regex: 'SHARP|SBM'
-  device: 'smartphone'
-  models: 
-    - regex: 'SHARP-AQUOS'
-      device: 'tv'
-      model: 'Aquos Net Plus'
-    - regex: 'SHARP[ \-]([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: '(?:SHARP|SBM)([A-Za-z0-9]+)'
-      model: '$1'
-
-# Softbank
-Softbank:
-  regex: 'Softbank|J-PHONE'
-  device: 'smartphone'
-  models: 
-    - regex: 'Softbank/[12]\.0/([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: '([A-Za-z0-9]+);Softbank;'
-      model: '$1'
-    - regex: 'J-PHONE/[0-9]\.[0-9]/([A-Za-z0-9\-]+)'
-      model: '$1'
-
-# Sony
-Sony:
-  regex: 'Sony|PlayStation'
-  device: 'smartphone'
-  models: 
-    - regex: 'Sony[ ]?([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: '(PlayStation (?:3|Portable|Vita))'
-      device: 'console'
-      model: '$1'
-
-# Qtek
-Qtek:
-  regex: 'Qtek[ _]?([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# T-Mobile
-T-Mobile:
-  regex: 'T-Mobile[ _]([A-Za-z0-9 ]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Tcl
-TCL:
-  regex: 'TCL-([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Telit
-Telit:
-  regex: 'Telit'
-  device: 'feature phone'
-  models:
-    - regex: 'Telit_Mobile_Terminals-([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'Telit[\-_]?([A-Za-z0-9]+)'
-      model: '$1'
-
-# Tianyu
-TIANYU:
-  regex: 'TIANYU'
-  device: 'feature phone'
-  models:
-    - regex: 'TIANYU ([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'TIANYU-KTOUCH/([A-Za-z0-9]+)'
-      model: '$1'
-
-# Toplux
-Toplux:
-  regex: 'Toplux ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# UTStarcom
-UTStarcom:
-  regex: 'utstar([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Vitelcom
-Vitelcom:
-  regex: 'Vitelcom|portalmmm/[12].0 TSM'
-  device: 'feature phone'
-  models:
-    - regex: 'TSM-([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'TSM([A-Za-z0-9\-]+)'
-      model: '$1'
-    - regex: 'portalmmm/[12].0 (TSM[A-Za-z0-9 ]+)'
-      model: '$1'
-
-# VK Mobile
-VK Mobile:
-  regex: 'VK[\-]?([A-Za-z0-9 ]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Vertu
-Vertu:
-  regex: 'Vertu[ ]?([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Videocon
-Videocon:
-  regex: 'Videocon_([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Voxtel
-Voxtel:
-  regex: 'Voxtel_([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Wellcom
-WellcoM:
-  regex: 'WELLCOM[ _\-/]([A-Za-z0-9]+)'
-  device: 'smartphone'
-  model: '$1'
-
-# Wonu
-Wonu:
-  regex: 'Wonu ([A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Zonda
-Zonda:
-  regex: '(ZM(?:CK|EM|TFTV|TN)[A-Za-z0-9]+)'
-  device: 'feature phone'
-  model: '$1'
-
-# Toshiba
-Toshiba:
-  regex: 'Toshiba|portalmmm/[12].0 TS'
-  device: 'smartphone'
-  models:
-    - regex: 'Toshiba[ /_\-]?([A-Za-z0-9 ]+)'
-      model: '$1'
-    - regex: 'portalmmm/[12].0 (TS[A-Za-z0-9 ]+)'
-      model: '$1'
-
-# Fly
-Fly:
-  regex: 'Fly|MERIDIAN-'
-  device: 'smartphone'
-  models:
-    - regex: 'Fly[ _\-]?([A-Za-z0-9]+)'
-      model: '$1'
-    - regex: 'MERIDIAN-([A-Za-z0-9]+)'
-      model: '$1'
-
-# WebTV
-WebTV:
-  regex: 'WebTV/(\d+\.\d+)'
-  device: 'tv'
-  model: '$1'
-
-# ZTE
-ZTE:
-  regex: 'ZTE|Z331'
-  device: 'smartphone'
-  models:
-    - regex: '(Z331)'
-      model: '$1'
-    - regex: 'ZTE-(?:G |G-)?([A-Za-z0-9 _]+)'
-      model: '$1'
-    - regex: 'ZTE ([A-Za-z0-9]+)'
-      model: '$1'
-  
-# Symbian to Nokia ??
-# Change name from Nokia to other to not change above Nokia element
-#Nokia:
-#  regex: 'Symbian'
+###############
+# Piwik - Open source web analytics
+#
+# @link http://piwik.org
+# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+#
+# @category Piwik_Plugins
+# @package Piwik_DevicesDetection
+###############
+
+# HTC
+HTC:
+  regex: 'HTC|Sprint APA|ADR[A-Za-z0-9]+'
+  device: 'smartphone'
+  models:
+    - regex: 'HTC ([A-Za-z0-9]+) Build'
+      model: '$1'
+    - regex: 'HTC ([A-Za-z0-9]+(?: [A-Za-z0-9]+)?)'
+      model: '$1'
+    - regex: 'USCCHTC(\d+)'
+      model: '$1'
+    - regex: 'Sprint APA(9292)'
+      model: '$1 (Sprint)'
+    - regex: 'HTC_([A-Za-z0-9_]+)'
+      model: '$1'
+    - regex: 'HTC(?:[\-/ ])?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'HTC;(?: )?([A-Za-z0-9 ]+)'
+      model: '$1'
+    - regex: '(ADR[A-Za-z0-9]+)'
+      model: '$1'
+
+# Tesla Model S
+Tesla:
+  regex: 'QtCarBrowser'
+  device: 'car browser'
+  model: 'Model S'
+
+# Kindle
+Kindle:
+  regex: 'KF(?:OT|TT|JWI|JWA) Build|Kindle|Silk/(\d+)\.(\d+)'
+  device: 'tablet'
+  models:
+    - regex: 'KFOT|Kindle Fire|Silk/(\d+)\.(\d+)'
+      model: 'Fire'
+    - regex: 'KFTT'
+      model: 'Fire HD'
+    - regex: 'KFJWI'
+      model: 'Fire HD 8.9" WiFi'
+    - regex: 'KFJWA'
+      model: 'Fire HD 8.9" 4G'
+
+# NOKIA
+Nokia:
+  regex: 'Nokia|Lumia|Maemo RX|portalmmm/2\.0 N7|portalmmm/2\.0 NK|nok[0-9]+|Symbian.*\s([a-zA-Z0-9]+)$'
+  device: 'smartphone'
+  models:
+    - regex: 'NokiaInternal|Nokia-WAP-Toolkit|Nokia-MIT-Browser|Nokia Mobile|Nokia Browser|Nokia/Series'
+      model: ''
+    - regex: 'Nokia(N[0-9]+)'
+      model: '$1'
+    - regex: 'Nokia-([A-Za-z0-9]+)'
+      model: 'N$1'
+    - regex: 'NOKIA; ([A-Za-z0-9\- ]+)'
+      model: '$1'
+    - regex: 'NOKIA[ ]?([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'NOKIA/([A-Za-z0-9 ]+)'
+      model: '$1'
+    - regex: '(Lumia [A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'Maemo RX-51 ([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'Maemo RX-34'
+      model: 'N800'
+    - regex: 'portalmmm/2\.0 (N7[37]|NK[A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'nok([0-9]+)'
+      model: '$1'
+    - regex: 'Symbian.*\s([a-zA-Z0-9]+)$'
+      device: 'feature phone'
+      model: '$1'
+  
+# RIM/BlackBerry
+RIM:
+  regex: 'BB10;|BlackBerry|rim[0-9]+|PlayBook'
+  device: 'smartphone'
+   
+  models:
+    - regex: 'BB10; ([A-Za-z0-9\- ]+)\)'
+      model: 'BlackBerry $1'
+    - regex: 'PlayBook.+RIM Tablet OS'
+      model: 'BlackBerry Playbook'
+      device: 'tablet'
+    - regex: 'BlackBerry(?: )?([A-Za-z0-9]+)'
+      model: 'BlackBerry $1'
+    - regex: 'rim([0-9]+)'
+      model: 'BlackBerry $1'
+    - regex: 'BlackBerry'
+      model: 'BlackBerry'
+
+# PALM
+Palm:
+  regex: '(?:Pre|Pixi)/(\d+)\.(\d+)|Palm|Treo'
+  device: 'smartphone'
+  models:
+    - regex: '((?:Pre|Pixi))/(\d+\.\d+)'
+      model: '$1 $2'
+    - regex: 'Palm(?: )?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'Treo([A-Za-z0-9]+)'
+      model: 'Treo $1'
+
+# HP
+HP:
+  regex: 'Touch[Pp]ad|hp-tablet|HP(?: )?iPAQ|webOS.*(P160U)'
+  device: 'smartphone'
+  models:
+    - regex: 'Touch[Pp]ad/(\d+\.\d+)|hp-tablet'
+      model: 'TouchPad'
+      device: 'tablet'
+    - regex: 'HP(?: )?iPAQ(?: )?([A-Za-z0-9]+)'
+      model: 'iPAQ $1'
+    - regex: 'webOS.*(P160U)'
+      model: 'Veer'
+
+# TiPhone
+TiPhone:
+  regex: 'TiPhone(?: )?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Apple
+Apple:
+  regex: 'AppleTV|iPad|iPod|iPhone'
+  models:
+    - regex: 'AppleTV'
+      model: 'Apple TV'
+      device: 'tv'
+    - regex: 'iPad'
+      model: 'iPad'
+      device: 'tablet'
+    - regex: 'iPod'
+      model: 'iPod Touch'
+      device: 'palmtop'
+    - regex: 'iPhone'
+      model: 'iPhone'
+      device: 'smartphone'
+
+# Acer
+Acer:
+  regex: 'acer[\-_]([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+   
+# Airness
+Airness:
+  regex: 'AIRNESS-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+   
+# Alcatel
+Alcatel:
+  regex: 'Alcatel|Alc([A-Za-z0-9]+)'
+  device: 'smartphone'
+  models:
+    - regex: 'Alcatel UP'
+      model: ''
+    - regex: 'ALCATEL[ \-]([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'ALCATEL_([A-Za-z0-9_]+)'
+      model: '$1'
+    - regex: 'Alc([A-Za-z0-9]+)'
+      model: '$1'
+
+# Amoi
+Amoi:
+  regex: 'Amoi'
+  device: 'smartphone'
+  models:
+    - regex: 'Amoi[\- /](A-Za-z0-9]+)'
+      mobile: '$1'
+    - regex: 'Amoisonic-([A-Za-z0-9]+)'
+      model: '$1'
+
+# Asus
+Asus:
+  regex: 'Asus'
+  device: 'smartphone'
+  models:
+    - regex: 'Asus(?:-|;)?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'ASUS (Transformer Pad TF300T)'
+      device: 'tablet'
+      model: '$1'
+
+# Audiovox
+Audiovox:
+  regex: 'Audiovox|CDM|UTS(?:TARCOM)?\-|audio([A-Za-z0-9\-]+)'
+  device: 'smartphone'
+  models:
+    - regex: 'Audiovox[_\-]([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'CDM(?:-)?([A-Za-z0-9]+)'
+      model: 'CDM-$1'
+    - regex: 'UTS(?:TARCOM)?-([A-Za-z0-9\-]+)'
+      model: 'CDM-$1'
+    - regex: 'audio([A-Za-z0-9\-]+)'
+      model: 'CDM-$1'
+
+# Avvio
+Avvio:
+  regex: 'Avvio[ _]([A-Za-z0-9\-]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Bird
+Bird:
+  regex: 'BIRD[\-. _]([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Becker
+Becker:
+  regex: 'Becker-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Beetel
+Beetel:
+  regex: 'Beetel ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+  
+# BenQ-Siemens
+BenQ-Siemens:
+  regex: 'BENQ-SIEMENS - ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+  
+# BenQ
+BenQ:
+  regex: 'BENQ(?:[ \-])?([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Capitel
+Capitel:
+  regex: 'Capitel-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Compal
+Compal:
+  regex: 'Compal-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Cricket
+Cricket:
+  regex: 'Cricket-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Dell
+Dell:
+  regex: 'Dell ([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Dbtel
+Dbtel:
+  regex: 'DBTEL(?:[\-/])?([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Dicam
+Dicam:
+  regex: 'DICAM-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# DoCoMo
+DoCoMo:
+  regex: 'DoCoMo|\;FOMA|KGT/1\.0'
+  device: 'feature phone'
+  models:
+    - regex: 'DoCoMo/[12]\.0[/ ]([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '([A-Za-z0-9]+)(?:_W)?\;FOMA'
+      model: '$1'
+    - regex: 'KGT/1\.0 ([A-Za-z0-9]+)'
+      model: '$1'
+
+# Dopod
+Dopod:
+  regex: 'Dopod(?: )?([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Ericy
+Ericy:
+  regex: 'Ericy-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Sony Ericsson
+Sony Ericsson:
+  regex: 'Sony(?: )?Ericsson|portalmmm/2\.0 K'
+  device: 'smartphone'
+  models:
+    - regex: 'SonyEricsson([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'Sony(?: )?Ericsson ([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'portalmmm/2.0 K([A-Za-z0-9]+)'
+      model: 'K$1'
+
+# Ericsson
+Ericsson:
+  regex: 'Ericsson(?:/ )?([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# eTouch
+eTouch:
+  regex: 'eTouch(?: )?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Ezze
+Ezze:
+  regex: 'EZZE-|EZ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  models: 
+    - regex: 'EZZE-([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'EZ([A-Za-z0-9]+)'
+      model: 'EZ$1'
+
+# Ezio
+Ezio:
+  regex: 'EZIO-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Gionee
+Gionee:
+  regex: 'GIONEE-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Google
+Google:
+  regex: 'Nexus|GoogleTV'
+  device: 'smartphone'
+  models:
+    - regex: '(Galaxy Nexus)'
+      model: '$1'
+    - regex: '(Nexus (:?S|4|One))'
+      model: '$1'
+    - regex: '(Nexus (:?7|10))'
+      device: 'tablet'
+      model: '$1'
+    - regex: '(GoogleTV)'
+      device: 'tv'
+      model: '$1'
+
+# Gradiente
+Gradiente:
+  regex: 'GRADIENTE'
+  device: 'feature phone'
+  models: 
+    - regex: 'GRADIENTE-([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'GRADIENTE ([A-Za-z0-9\-]+)'
+      model: '$1'
+
+# Grundig
+Grundig:
+  regex: 'GRUNDIG|portalmmm/2\.0 G'
+  device: 'tv'
+  models: 
+    - regex: 'GRUNDIG ([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'portalmmm/2\.0 G([A-Za-z0-9]+)'
+      model: 'G$1'
+
+# Haier
+Haier:
+  regex: 'Haier[ -]([A-Za-z0-9\-]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Huawei
+Huawei:
+  regex: 'Huawei|vodafone([A-Za-z0-9]+)'
+  device: 'smartphone'
+  models:
+    - regex: 'Huawei(?:[\- /_]|/1\.0/)?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'vodafone([A-Za-z0-9]+)'
+      model: 'Vodafone $1'
+
+# Innostream
+Innostream:
+  regex: 'INNO([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: 'INNO$1'
+
+# Inq
+INQ:
+  regex: 'INQ/([A-Za-z0-9\-]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# i-mate
+i-mate:
+  regex: 'i-mate ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# i-mobile
+i-mobile:
+  regex: 'i-mobile(?: )?([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# ikomo
+iKoMo:
+  regex: 'iKoMo ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# kddi
+KDDI:
+  regex: 'kddi-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# kyocera
+Kyocera:
+  regex: 'Kyocera|KWC-|QC-'
+  device: 'smartphone'
+  models:
+    - regex: 'Kyocera-KZ-([A-Za-z0-9]+)'
+      model: 'KZ $1'
+    - regex: 'Kyocera(:?[\-/])?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '(?:KWC|QC)-([A-Za-z0-9]+)'
+      model: '$1'
+
+# lanix
+Lanix:
+  regex: 'LANIX-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# lct
+LCT:
+  regex: 'LCT_([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# lenovo
+Lenovo:
+  regex: 'Lenovo[\-_]([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# lguplus
+LGUPlus:
+  regex: 'LGUPlus'
+  device: 'smartphone'
+  model: ''
+
+# lg
+LG:
+  regex: 'LG|portalmmm/2\.0 (?:KE|KG|KP|L3)|VX[0-9]+'
+  device: 'smartphone'
+  models:
+    - regex: 'LGE_DLNA_SDK'
+      device: 'tv'
+      model: 'NetCast'
+    - regex: 'LGE(?: |-LG| LG-AX|-)([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'LGE;([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'LG(?:/|-LG| |-)?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'LG; ([A-Za-z0-9 ]+)'
+      model: '$1'
+    - regex: 'portalmmm/2.0 ((?:KE|KG|KP|L3)[A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '(VX[0-9]+)'
+      model: '$1'
+
+# microsoft
+Microsoft:
+  regex: 'Xbox|KIN\.(?:One|Two)'
+  device: 'console'
+  model: 'Xbox 360'
+
+# Konka
+Konka:
+  regex: 'KONKA_([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Karbonn
+Karbonn:
+  regex: 'Karbonn_([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Sagem
+Sagem:
+  regex: 'SAGEM|portalmmm/2.0 (?:SG|my)'
+  device: 'smartphone'
+  models:
+    - regex: 'SAGEM ([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'SAGEM-([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'portalmmm/2.0 ((?:SG|my)[A-Za-z0-9]+)'
+      model: '$1'
+
+# micromax
+MicroMax:
+  regex: 'MicroMax(?:[ \-])?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# mio
+Mio:
+  regex: 'MIO(?:/)?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# mitsubishi
+Mitsubishi:
+  regex: 'MITSU|portalmmm/[12]\.0 M'
+  device: 'feature phone'
+  models:
+    - regex: 'MITSU/[A-Za-z0-9.]+ \(([A-Za-z0-9]+)\)'
+      model: '$1'
+    - regex: 'MITSU[ \-]?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'portalmmm/[12]\.0 (M[A-Za-z0-9]+)'
+      model: '$1'
+
+# motorola
+Motorola:
+  regex: 'MOT|(?<!AN)DROID (?:Build|([A-Za-z0-9]+))|portalmmm/2.0 (?:E378i|L6|L7|v3)'
+  device: 'smartphone'
+  models:
+    - regex: 'Motorola[ \-]([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'MOTORAZR[ \-]([A-Za-z0-9]+)'
+      model: 'RAZR $1'
+    - regex: 'MOTORIZR[ \-]([A-Za-z0-9]+)'
+      model: 'RIZR $1'
+    - regex: 'MOT[O]?[\-]?([A-Za-z0-9.]+)'
+      model: '$1'
+    - regex: '(?<!AN)DROID (?:Build|([A-Za-z0-9]+))'
+      model: 'DROID $1'
+    - regex: 'portalmmm/2.0 ((?:E378i|L6|L7|V3)[A-Za-z0-9]+)'
+      model: '$1'
+
+# myphone
+MyPhone:
+  regex: 'MyPhone([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# nec
+NEC:
+  regex: 'NEC|KGT/2\.0|portalmmm/1\.0 (?:DB|N)|(?:portalmmm|o2imode)/2.0[ ,]N'
+  device: 'smartphone'
+  models:
+    - regex: '(?:NEC-|KGT/2\.0 )([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'portalmmm/1\.0 ((?:DB|N)[A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '(?:portalmmm|o2imode)/2\.0[ ,](N[A-Za-z0-9]+)'
+      model: '$1'
+
+# newgen
+Newgen:
+  regex: 'NEWGEN\-([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+  
+# nintendo
+Nintendo:
+  regex: 'Nintendo (([3]?DS[i]?)|Wii[U]?)'
+  device: 'console'
+  model: '$1'
+  
+# ngm
+NGM:
+  regex: 'NGM_([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+  
+# nexian
+Nexian:
+  regex: 'Nexian'
+  device: 'smartphone'
+  models:
+    - regex: 'Nexian[ ]?([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'Nexian-([A-Za-z0-9]+)'
+      model: '$1'
+  
+# o2
+O2:
+  regex: 'Xda|O2[ \-]|COCOON'
+  device: 'smartphone'
+  models:
+    - regex: '(Xda[ _][A-Za-z0-9_]+)'
+      models: '$1'
+    - regex: '(COCOON)'
+      models: '$1'
+    - regex: 'O2 ([A-Za-z0-9 ]+)'
+      models: '$1'
+    - regex: 'O2-([A-Za-z0-9]+)'
+      models: '$1'
+  
+# onda
+Onda:
+  regex: 'Onda'
+  device: 'smartphone'
+  models:
+    regex: '([A-Za-z0-9]+)[ _]Onda'
+    model: '$1'
+    regex: 'Onda ([A-Za-z0-9]+)'
+    model: '$1'
+  
+# oppo
+OPPO:
+  regex: 'OPPO[ ]?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+  
+# orange
+Orange:
+  regex: 'SPV[ \-]?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: 'SPV $1'
+
+# panasonic
+Panasonic:
+  regex: 'Panasonic'
+  device: 'smartphone'
+  models:
+    - regex: 'Panasonic MIL DLNA'
+      device: 'tv'
+      model: 'Viera Cast'
+    - regex: 'Panasonic[ \-]?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'portalmmm/2.0 (P[A-Za-z0-9]+)'
+      model: '$1'
+ 
+# philips
+Philips:
+  regex: 'Philips'
+  device: 'smartphone'
+  models:
+    - regex: 'Philips-FISIO ([A-Za-z0-9]+)'
+      model: 'Fisio $1'
+    - regex: 'Philips[ ]?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'Philips-([A-Za-z0-9\-@]+)'
+      model: '$1'
+ 
+# phoneOne
+phoneOne:
+  regex: 'phoneOne[ \-]?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+ 
+# Rover
+Rover:
+  regex: 'Rover ([0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Siemens
+Siemens:
+  regex: 'SIEMENS|SIE-|portalmmm/2\.0 SI|S55|SL45i'
+  device: 'smartphone'
+  models:
+    - regex: 'SIEMENS[ \-]([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'SIE(?:MENS )?[\-]?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '(S55|SL45i)'
+      model: '$1'
+    - regex: 'portalmmm/2.0 (SI[A-Za-z0-9]+)'
+      model: '$1'
+
+# Samsung
+Samsung:
+  regex: 'SAMSUNG|S(?:CH|GH|PH|EC|AM)-|SMART-TV|GT-|Galaxy|(?:portalmmm|o2imode)/2\.0 [SZ]|sam[rua]'
+  device: 'smartphone'
+  models:
+    - regex: 'SAMSUNG[\-;][ ]?([A-Za-z0-9]+[\-_][A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'SAMSUNG[ _/]?([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'SAMSUNG;[ ]?([A-Za-z0-9 ]+)'
+      model: '$1'
+    - regex: '((?:SCH|SGH|SPH|GT)-[A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'SEC-([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'SAM-([A-Za-z0-9]+)'
+      model: 'SCH-$1'
+    - regex: 'SMART-TV'
+      device: 'tv'
+      model: 'Smart TV'
+    - regex: '(Galaxy [A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '(?:portalmmm|o2imode)/2\.0 ([SZ][A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'sam([rua][0-9]+)'
+      model: 'SCH-$1'
+
+# pantech
+Pantech:
+  regex: 'Pantech|P[GTN]-|TX[T]?[0-9]+'
+  device: 'smartphone'
+  models:
+    - regex: 'Pantech[ \-]?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'Pantech_([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: '(P[GTN]-[A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '(TX[T]?[0-9]+)'
+      model: '$1'  
+
+# Sanyo
+Sanyo:
+  regex: 'Sanyo|MobilePhone '
+  device: 'smartphone'
+  models:
+    - regex: 'SANYO[ \-_]([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'MobilePhone ([A-Za-z0-9\-]+)'
+      model: '$1'
+
+# Sega
+Sega:
+  regex: 'Dreamcast'
+  device: 'console'
+  model: 'Dreamcast'
+
+# Sendo
+Sendo:
+  regex: 'Sendo([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Spice
+Spice:
+  regex: 'Spice'
+  device: 'smartphone'
+  models: 
+    - regex: 'Spice ([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'Spice-([A-Za-z0-9]+)'
+      model: '$1'
+  
+# Sharp
+Sharp:
+  regex: 'SHARP|SBM'
+  device: 'smartphone'
+  models: 
+    - regex: 'SHARP-AQUOS'
+      device: 'tv'
+      model: 'Aquos Net Plus'
+    - regex: 'SHARP[ \-]([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: '(?:SHARP|SBM)([A-Za-z0-9]+)'
+      model: '$1'
+
+# Softbank
+Softbank:
+  regex: 'Softbank|J-PHONE'
+  device: 'smartphone'
+  models: 
+    - regex: 'Softbank/[12]\.0/([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: '([A-Za-z0-9]+);Softbank;'
+      model: '$1'
+    - regex: 'J-PHONE/[0-9]\.[0-9]/([A-Za-z0-9\-]+)'
+      model: '$1'
+
+# Sony
+Sony:
+  regex: 'Sony|PlayStation'
+  device: 'smartphone'
+  models: 
+    - regex: 'Sony[ ]?([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: '(PlayStation (?:3|Portable|Vita))'
+      device: 'console'
+      model: '$1'
+
+# Qtek
+Qtek:
+  regex: 'Qtek[ _]?([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# T-Mobile
+T-Mobile:
+  regex: 'T-Mobile[ _]([A-Za-z0-9 ]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Tcl
+TCL:
+  regex: 'TCL-([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Telit
+Telit:
+  regex: 'Telit'
+  device: 'feature phone'
+  models:
+    - regex: 'Telit_Mobile_Terminals-([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'Telit[\-_]?([A-Za-z0-9]+)'
+      model: '$1'
+
+# Tianyu
+TIANYU:
+  regex: 'TIANYU'
+  device: 'feature phone'
+  models:
+    - regex: 'TIANYU ([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'TIANYU-KTOUCH/([A-Za-z0-9]+)'
+      model: '$1'
+
+# Toplux
+Toplux:
+  regex: 'Toplux ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# UTStarcom
+UTStarcom:
+  regex: 'utstar([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Vitelcom
+Vitelcom:
+  regex: 'Vitelcom|portalmmm/[12].0 TSM'
+  device: 'feature phone'
+  models:
+    - regex: 'TSM-([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'TSM([A-Za-z0-9\-]+)'
+      model: '$1'
+    - regex: 'portalmmm/[12].0 (TSM[A-Za-z0-9 ]+)'
+      model: '$1'
+
+# VK Mobile
+VK Mobile:
+  regex: 'VK[\-]?([A-Za-z0-9 ]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Vertu
+Vertu:
+  regex: 'Vertu[ ]?([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Videocon
+Videocon:
+  regex: 'Videocon_([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Voxtel
+Voxtel:
+  regex: 'Voxtel_([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Wellcom
+WellcoM:
+  regex: 'WELLCOM[ _\-/]([A-Za-z0-9]+)'
+  device: 'smartphone'
+  model: '$1'
+
+# Wonu
+Wonu:
+  regex: 'Wonu ([A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Zonda
+Zonda:
+  regex: '(ZM(?:CK|EM|TFTV|TN)[A-Za-z0-9]+)'
+  device: 'feature phone'
+  model: '$1'
+
+# Toshiba
+Toshiba:
+  regex: 'Toshiba|portalmmm/[12].0 TS'
+  device: 'smartphone'
+  models:
+    - regex: 'Toshiba[ /_\-]?([A-Za-z0-9 ]+)'
+      model: '$1'
+    - regex: 'portalmmm/[12].0 (TS[A-Za-z0-9 ]+)'
+      model: '$1'
+
+# Fly
+Fly:
+  regex: 'Fly|MERIDIAN-'
+  device: 'smartphone'
+  models:
+    - regex: 'Fly[ _\-]?([A-Za-z0-9]+)'
+      model: '$1'
+    - regex: 'MERIDIAN-([A-Za-z0-9]+)'
+      model: '$1'
+
+# WebTV
+WebTV:
+  regex: 'WebTV/(\d+\.\d+)'
+  device: 'tv'
+  model: '$1'
+
+# ZTE
+ZTE:
+  regex: 'ZTE|Z331'
+  device: 'smartphone'
+  models:
+    - regex: '(Z331)'
+      model: '$1'
+    - regex: 'ZTE-(?:G |G-)?([A-Za-z0-9 _]+)'
+      model: '$1'
+    - regex: 'ZTE ([A-Za-z0-9]+)'
+      model: '$1'
+  
+# Symbian to Nokia ??
+# Change name from Nokia to other to not change above Nokia element
+#Nokia:
+#  regex: 'Symbian'
 #  device: 'feature phone'
\ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml
index f1660d636f..71b85f987f 100644
--- a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml
@@ -1,427 +1,427 @@
-###############
-# Piwik - Open source web analytics
-#
-# @link http://piwik.org
-# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
-#
-# @category Piwik_Plugins
-# @package Piwik_DevicesDetection
-###############
-
-##########
-# Tizen
-##########
-- regex: 'Tizen'
-  name: 'Tizen'
-  version: ''
-  
-  
-  
-##########
-# Android
-##########
-- regex: 'Android[ /](\d+\.\d+)'
-  name: 'Android'
-  version: '$1'
-  
-  
-- regex: 'Android|Silk-Accelerated=[a-z]{4,5}'
-  name: 'Android'
-  version: ''
-  
-  
-  
-##########
-# Linux
-##########
-- regex: 'Linux; .*((?:Arch Linux|Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Slackware|SUSE|Puppy|CentOS|BackTrack|YunOs|Presto))[ /](\d+\.\d+)'
-  name: '$1'
-  version: '$2'
-  
-  
-- regex: '((?:Arch Linux|Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Slackware|SUSE|Puppy|CentOS|BackTrack|YunOs|Presto));.*Linux'
-  name: '$1'
-  version: ''
-  
-  
-- regex: 'Linux; |Linux (?:x86_64|zbov|i686)'
-  name: 'Linux'
-  version: ''
-  
-  
-  
-##########
-# Windows Mobile
-##########
-- regex: 'Windows Phone (?:OS)?[ ]?(\d+\.\d+)'
-  name: 'Windows Phone'
-  version: '$1'
-  
-  
-- regex: 'XBLWP7|Windows Phone'
-  name: 'Windows Phone'
-  version: ''
-
-- regex: 'Windows CE'
-  name: 'Windows CE'
-  version: ''
-  
-  
-- regex: '(?:IEMobile|Windows Mobile)(?: (\d+\.\d+))?'
-  name: 'Windows Mobile'
-  version: '$1'
-  
-  
-- regex: 'Windows NT 6.2; ARM;'
-  name: 'Windows RT'
-  version: ''
-  
-  
-  
-##########
-# Windows
-##########
-- regex: 'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8'
-  name: 'Windows 8'
-  version: '8'
-  
-  
-- regex: 'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7'
-  name: 'Windows 7'
-  version: '7'
-  
-  
-- regex: 'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista'
-  name: 'Windows Vista'
-  version: 'Vista'
-  
-  
-- regex: 'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64'
-  name: 'Windows Server 2003'
-  version: 'Server 2003'
-  
-  
-- regex: 'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'
-  name: 'Windows XP'
-  version: 'XP'
-  
-  
-- regex: 'CYGWIN_NT-5.0|Windows NT 5.0|Windows 2000'
-  name: 'Windows 2000'
-  version: '2000'
-  
-  
-- regex: 'CYGWIN_NT-4.0|Windows NT 4.0|WinNT|Windows NT'
-  name: 'Windows NT'
-  version: 'NT'
-  
-  
-- regex: 'CYGWIN_ME-4.90|Win 9x 4.90|Windows ME'
-  name: 'Windows ME'
-  version: 'ME'
-  
-  
-- regex: 'CYGWIN_98-4.10|Win98|Windows 98'
-  name: 'Windows 98'
-  version: '98'
-  
-  
-- regex: 'CYGWIN_95-4.0|Win32|Win95|Windows 95|Windows_95'
-  name: 'Windows 95'
-  version: '95'
-  
-  
-- regex: 'Windows 3.1'
-  name: 'Windows 3.1'
-  version: '3.1'
-  
-  
-- regex: 'Windows'
-  name: 'Windows'
-  version: ''
-  
-  
-  
-##########
-# Mac
-##########
-- regex: 'Mac OS X (\d+[_.]\d+)'
-  name: 'Mac'
-  
-  version: '$1'
-  
-- regex: 'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC'
-  name: 'Mac'
-  version: ''
-  
-  
-  
-##########
-# iOS
-##########
-- regex: '(?:CPU OS|iPhone OS) (\d+_\d+)'
-  name: 'iOS'
-  
-  version: '$1'
-  
-- regex: '(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\d+\.\d+)|; Opera)'
-  name: 'iOS'
-  version: '$1'
-  
-  
-  
-##########
-# webOS
-##########
-- regex: '(?:webOS|Palm webOS)(?:/(\d+\.\d+))?'
-  name: 'webOS'
-  version: '$1'
-  
-  
-- regex: '(?:PalmOS|Palm OS)(?:/(\d+\.\d+))?'
-  name: 'PalmOS'
-  version: ''
-  
-  
-  
-##########
-# ChromeOS
-##########
-- regex: 'CrOS [a-z0-9_]+ (\d+\.\d+)'
-  name: 'Chrome OS'
-  version: '$1'
-  
-  
-  
-##########
-# BlackBerry
-##########
-- regex: '(?:BB10;.+Version|Black[Bb]erry[0-9a-z]+|Black[Bb]erry.+Version)/(\d+\.\d+)'
-  name: 'BlackBerry OS'
-  version: '$1'
-  
-  
-- regex: 'RIM Tablet OS (\d+\.\d+)'
-  name: 'BlackBerry Tablet OS'
-  version: '$1'
-  
-  
-- regex: 'RIM Tablet OS|QNX|Play[Bb]ook'
-  name: 'BlackBerry Tablet OS'
-  version: ''
-  
-  
-- regex: 'Black[Bb]erry'
-  name: 'BlackBerry OS'
-  version: ''
-  
-  
-  
-##########
-# Symbian
-##########
-- regex: 'Symbian[Oo][Ss]/(\d+\.\d+)'
-  name: 'Symbian OS'
-  version: '$1'
-  
-  
-- regex: 'Symbian/3.+NokiaBrowser/7\.3'
-  name: 'Symbian'
-  version: '^3 Anna'
-  
-  
-- regex: 'Symbian/3.+NokiaBrowser/7\.4'
-  name: 'Symbian'
-  version: '^3 Belle'
-  
-  
-- regex: 'Symbian[/]?3'
-  name: 'Symbian^3'
-  version: '^3'
-  
-  
-- regex: '(?:Series 60|SymbOS|S60)(?:[ /]?(\d+\.\d+|V\d+))?'
-  name: 'Symbian OS Series 60'
-  version: '$1'
-  
-  
-- regex: 'Series40'
-  name: 'Symbian OS Series 40'
-  version: ''
-  
-  
-- regex: 'MeeGo|WeTab'
-  name: 'MeeGo'
-  version: ''
-  
-  
-- regex: 'Symbian [Oo][Ss]|SymbOS'
-  name: 'Symbian OS'
-  version: ''
-  
-  
-- regex: 'Nokia'
-  name: 'Symbian'
-  version: ''
-
-  
-
-##########
-# Firefox OS
-##########
-- regex: '(?:Mobile|Tablet);.+Firefox/\d+\.\d+'
-  name: 'Firefox OS'
-  version: ''
-  
-  
-  
-##########
-# Bada
-##########
-- regex: 'bada'
-  name: 'Bada'
-  version: ''
-  
-  
-  
-##########
-# Brew
-##########
-- regex: '(?:Brew MP|BREW|BMP)(?:[ /](\d+\.\d+))?'
-  name: 'Brew'
-  version: '$1'
-  
-  
-  
-##########
-# Web TV
-##########
-- regex: 'GoogleTV[ /](\d+\.\d+)|GoogleTV'
-  name: 'Google TV'
-  version: '$1'
-  
-  
-- regex: 'AppleTV/(\d+\.\d+)'
-  name: 'Apple TV'
-  version: '$1'
-  
-  
-- regex: 'WebTV/(\d+\.\d+)'
-  name: 'WebTV'
-  version: '$1'
-  
-  
-  
-##########
-# Unix
-##########
-- regex: 'SunOS'
-  name: 'Solaris'
-  version: ''
-  
-  
-- regex: 'AIX'
-  name: 'AIX'
-  version: ''
-  
-  
-- regex: 'HP-UX'
-  name: 'HP-UX'
-  version: ''
-  
-  
-- regex: 'FreeBSD'
-  name: 'FreeBSD'
-  version: ''
-  
-  
-- regex: 'NetBSD'
-  name: 'NetBSD'
-  version: ''
-  
-  
-- regex: 'OpenBSD'
-  name: 'OpenBSD'
-  version: ''
-  
-  
-- regex: 'DragonFly'
-  name: 'DragonFly'
-  version: ''
-  
-  
-- regex: 'Syllable'
-  name: 'Syllable'
-  version: ''
-  
-  
-- regex: 'IRIX'
-  name: 'IRIX'
-  version: ''
-  
-  
-- regex: 'OSF1'
-  name: 'OSF1'
-  version: ''
-  
-  
-  
-##########
-# Gaming Console
-##########
-- regex: 'Nintendo Wii'
-  name: 'Nintendo'
-  version: 'Wii'
-  
-  
-- regex: 'PlayStation 3|PlayStation3'
-  name: 'PlayStation'
-  version: '3'
-  
-  
-- regex: 'Xbox|KIN\.(?:One|Two)'
-  name: 'Xbox'
-  version: '360'
-  
-  
-  
-##########
-# Mobile Gaming Console
-##########
-- regex: 'Nitro|Nintendo ([3]?DS[i]?)'
-  name: 'Nintendo Mobile'
-  version: '$1'
-  
-  
-- regex: 'PlayStation ((?:Portable|Vita))'
-  name: 'PlayStation'
-  version: '$1'
-  
-  
-  
-##########
-# IBM
-##########
-- regex: 'OS/2'
-  name: 'OS/2'
-  version: ''
-  
-  
-  
-##########
-# Simulators
-##########
-- regex: '(Talkatone|WinWAP)'
-  name: '$1'
-  version: ''
-  
-  
-  
-##########
-# Bot
-##########
-- regex: '(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves/Teoma|ia_archiver|ScoutJet|Gulper Web Bot|EmailWolf|grub-client|Download Demon|OmniWeb|SearchExpress|Microsoft URL Control|bot|borg|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|transcoder|crawler|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast-webcrawler|converacrawler|dataparksearch|findlinksYottaaMonitor|BrowserMob|HttpMonitor|YandexBot|Slurp|BingPreview|PagePeeker|ThumbShotsBot|WebThumb|URL2PNG|ZooShot|GomezA|Catchpoint bot|Willow Internet Crawler|Google SketchUp|Read%20Later|Minimo|Pingdom.com|facebookexternalhit|Twitterbot|RackspaceBot)'
-  name: 'Bot  '
-  version: ''
-  
+###############
+# Piwik - Open source web analytics
+#
+# @link http://piwik.org
+# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+#
+# @category Piwik_Plugins
+# @package Piwik_DevicesDetection
+###############
+
+##########
+# Tizen
+##########
+- regex: 'Tizen'
+  name: 'Tizen'
+  version: ''
+  
+  
+  
+##########
+# Android
+##########
+- regex: 'Android[ /](\d+\.\d+)'
+  name: 'Android'
+  version: '$1'
+  
+  
+- regex: 'Android|Silk-Accelerated=[a-z]{4,5}'
+  name: 'Android'
+  version: ''
+  
+  
+  
+##########
+# Linux
+##########
+- regex: 'Linux; .*((?:Arch Linux|Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Slackware|SUSE|Puppy|CentOS|BackTrack|YunOs|Presto))[ /](\d+\.\d+)'
+  name: '$1'
+  version: '$2'
+  
+  
+- regex: '((?:Arch Linux|Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Slackware|SUSE|Puppy|CentOS|BackTrack|YunOs|Presto));.*Linux'
+  name: '$1'
+  version: ''
+  
+  
+- regex: 'Linux; |Linux (?:x86_64|zbov|i686)'
+  name: 'Linux'
+  version: ''
+  
+  
+  
+##########
+# Windows Mobile
+##########
+- regex: 'Windows Phone (?:OS)?[ ]?(\d+\.\d+)'
+  name: 'Windows Phone'
+  version: '$1'
+  
+  
+- regex: 'XBLWP7|Windows Phone'
+  name: 'Windows Phone'
+  version: ''
+
+- regex: 'Windows CE'
+  name: 'Windows CE'
+  version: ''
+  
+  
+- regex: '(?:IEMobile|Windows Mobile)(?: (\d+\.\d+))?'
+  name: 'Windows Mobile'
+  version: '$1'
+  
+  
+- regex: 'Windows NT 6.2; ARM;'
+  name: 'Windows RT'
+  version: ''
+  
+  
+  
+##########
+# Windows
+##########
+- regex: 'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8'
+  name: 'Windows 8'
+  version: '8'
+  
+  
+- regex: 'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7'
+  name: 'Windows 7'
+  version: '7'
+  
+  
+- regex: 'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista'
+  name: 'Windows Vista'
+  version: 'Vista'
+  
+  
+- regex: 'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64'
+  name: 'Windows Server 2003'
+  version: 'Server 2003'
+  
+  
+- regex: 'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'
+  name: 'Windows XP'
+  version: 'XP'
+  
+  
+- regex: 'CYGWIN_NT-5.0|Windows NT 5.0|Windows 2000'
+  name: 'Windows 2000'
+  version: '2000'
+  
+  
+- regex: 'CYGWIN_NT-4.0|Windows NT 4.0|WinNT|Windows NT'
+  name: 'Windows NT'
+  version: 'NT'
+  
+  
+- regex: 'CYGWIN_ME-4.90|Win 9x 4.90|Windows ME'
+  name: 'Windows ME'
+  version: 'ME'
+  
+  
+- regex: 'CYGWIN_98-4.10|Win98|Windows 98'
+  name: 'Windows 98'
+  version: '98'
+  
+  
+- regex: 'CYGWIN_95-4.0|Win32|Win95|Windows 95|Windows_95'
+  name: 'Windows 95'
+  version: '95'
+  
+  
+- regex: 'Windows 3.1'
+  name: 'Windows 3.1'
+  version: '3.1'
+  
+  
+- regex: 'Windows'
+  name: 'Windows'
+  version: ''
+  
+  
+  
+##########
+# Mac
+##########
+- regex: 'Mac OS X (\d+[_.]\d+)'
+  name: 'Mac'
+  
+  version: '$1'
+  
+- regex: 'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC'
+  name: 'Mac'
+  version: ''
+  
+  
+  
+##########
+# iOS
+##########
+- regex: '(?:CPU OS|iPhone OS) (\d+_\d+)'
+  name: 'iOS'
+  
+  version: '$1'
+  
+- regex: '(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\d+\.\d+)|; Opera)'
+  name: 'iOS'
+  version: '$1'
+  
+  
+  
+##########
+# webOS
+##########
+- regex: '(?:webOS|Palm webOS)(?:/(\d+\.\d+))?'
+  name: 'webOS'
+  version: '$1'
+  
+  
+- regex: '(?:PalmOS|Palm OS)(?:/(\d+\.\d+))?'
+  name: 'PalmOS'
+  version: ''
+  
+  
+  
+##########
+# ChromeOS
+##########
+- regex: 'CrOS [a-z0-9_]+ (\d+\.\d+)'
+  name: 'Chrome OS'
+  version: '$1'
+  
+  
+  
+##########
+# BlackBerry
+##########
+- regex: '(?:BB10;.+Version|Black[Bb]erry[0-9a-z]+|Black[Bb]erry.+Version)/(\d+\.\d+)'
+  name: 'BlackBerry OS'
+  version: '$1'
+  
+  
+- regex: 'RIM Tablet OS (\d+\.\d+)'
+  name: 'BlackBerry Tablet OS'
+  version: '$1'
+  
+  
+- regex: 'RIM Tablet OS|QNX|Play[Bb]ook'
+  name: 'BlackBerry Tablet OS'
+  version: ''
+  
+  
+- regex: 'Black[Bb]erry'
+  name: 'BlackBerry OS'
+  version: ''
+  
+  
+  
+##########
+# Symbian
+##########
+- regex: 'Symbian[Oo][Ss]/(\d+\.\d+)'
+  name: 'Symbian OS'
+  version: '$1'
+  
+  
+- regex: 'Symbian/3.+NokiaBrowser/7\.3'
+  name: 'Symbian'
+  version: '^3 Anna'
+  
+  
+- regex: 'Symbian/3.+NokiaBrowser/7\.4'
+  name: 'Symbian'
+  version: '^3 Belle'
+  
+  
+- regex: 'Symbian[/]?3'
+  name: 'Symbian^3'
+  version: '^3'
+  
+  
+- regex: '(?:Series 60|SymbOS|S60)(?:[ /]?(\d+\.\d+|V\d+))?'
+  name: 'Symbian OS Series 60'
+  version: '$1'
+  
+  
+- regex: 'Series40'
+  name: 'Symbian OS Series 40'
+  version: ''
+  
+  
+- regex: 'MeeGo|WeTab'
+  name: 'MeeGo'
+  version: ''
+  
+  
+- regex: 'Symbian [Oo][Ss]|SymbOS'
+  name: 'Symbian OS'
+  version: ''
+  
+  
+- regex: 'Nokia'
+  name: 'Symbian'
+  version: ''
+
+  
+
+##########
+# Firefox OS
+##########
+- regex: '(?:Mobile|Tablet);.+Firefox/\d+\.\d+'
+  name: 'Firefox OS'
+  version: ''
+  
+  
+  
+##########
+# Bada
+##########
+- regex: 'bada'
+  name: 'Bada'
+  version: ''
+  
+  
+  
+##########
+# Brew
+##########
+- regex: '(?:Brew MP|BREW|BMP)(?:[ /](\d+\.\d+))?'
+  name: 'Brew'
+  version: '$1'
+  
+  
+  
+##########
+# Web TV
+##########
+- regex: 'GoogleTV[ /](\d+\.\d+)|GoogleTV'
+  name: 'Google TV'
+  version: '$1'
+  
+  
+- regex: 'AppleTV/(\d+\.\d+)'
+  name: 'Apple TV'
+  version: '$1'
+  
+  
+- regex: 'WebTV/(\d+\.\d+)'
+  name: 'WebTV'
+  version: '$1'
+  
+  
+  
+##########
+# Unix
+##########
+- regex: 'SunOS'
+  name: 'Solaris'
+  version: ''
+  
+  
+- regex: 'AIX'
+  name: 'AIX'
+  version: ''
+  
+  
+- regex: 'HP-UX'
+  name: 'HP-UX'
+  version: ''
+  
+  
+- regex: 'FreeBSD'
+  name: 'FreeBSD'
+  version: ''
+  
+  
+- regex: 'NetBSD'
+  name: 'NetBSD'
+  version: ''
+  
+  
+- regex: 'OpenBSD'
+  name: 'OpenBSD'
+  version: ''
+  
+  
+- regex: 'DragonFly'
+  name: 'DragonFly'
+  version: ''
+  
+  
+- regex: 'Syllable'
+  name: 'Syllable'
+  version: ''
+  
+  
+- regex: 'IRIX'
+  name: 'IRIX'
+  version: ''
+  
+  
+- regex: 'OSF1'
+  name: 'OSF1'
+  version: ''
+  
+  
+  
+##########
+# Gaming Console
+##########
+- regex: 'Nintendo Wii'
+  name: 'Nintendo'
+  version: 'Wii'
+  
+  
+- regex: 'PlayStation 3|PlayStation3'
+  name: 'PlayStation'
+  version: '3'
+  
+  
+- regex: 'Xbox|KIN\.(?:One|Two)'
+  name: 'Xbox'
+  version: '360'
+  
+  
+  
+##########
+# Mobile Gaming Console
+##########
+- regex: 'Nitro|Nintendo ([3]?DS[i]?)'
+  name: 'Nintendo Mobile'
+  version: '$1'
+  
+  
+- regex: 'PlayStation ((?:Portable|Vita))'
+  name: 'PlayStation'
+  version: '$1'
+  
+  
+  
+##########
+# IBM
+##########
+- regex: 'OS/2'
+  name: 'OS/2'
+  version: ''
+  
+  
+  
+##########
+# Simulators
+##########
+- regex: '(Talkatone|WinWAP)'
+  name: '$1'
+  version: ''
+  
+  
+  
+##########
+# Bot
+##########
+- regex: '(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves/Teoma|ia_archiver|ScoutJet|Gulper Web Bot|EmailWolf|grub-client|Download Demon|OmniWeb|SearchExpress|Microsoft URL Control|bot|borg|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|transcoder|crawler|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast-webcrawler|converacrawler|dataparksearch|findlinksYottaaMonitor|BrowserMob|HttpMonitor|YandexBot|Slurp|BingPreview|PagePeeker|ThumbShotsBot|WebThumb|URL2PNG|ZooShot|GomezA|Catchpoint bot|Willow Internet Crawler|Google SketchUp|Read%20Later|Minimo|Pingdom.com|facebookexternalhit|Twitterbot|RackspaceBot)'
+  name: 'Bot  '
+  version: ''
+  
   
\ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/spyc.php b/plugins/DevicesDetection/UserAgentParserEnhanced/spyc.php
index ed3233ee1b..e19d562035 100644
--- a/plugins/DevicesDetection/UserAgentParserEnhanced/spyc.php
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/spyc.php
@@ -1,1046 +1,1046 @@
-<?php
-/**
-   * Spyc -- A Simple PHP YAML Class
-   * @version 0.5
-   * @author Vlad Andersen <vlad.andersen@gmail.com>
-   * @author Chris Wanstrath <chris@ozmm.org>
-   * @link http://code.google.com/p/spyc/
-   * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
-   * @license http://www.opensource.org/licenses/mit-license.php MIT License
-   * @package Spyc
-   */
-
-if (!function_exists('spyc_load')) {
-  /**
-   * Parses YAML to array.
-   * @param string $string YAML string.
-   * @return array
-   */
-  function spyc_load ($string) {
-    return Spyc::YAMLLoadString($string);
-  }
-}
-
-if (!function_exists('spyc_load_file')) {
-  /**
-   * Parses YAML to array.
-   * @param string $file Path to YAML file.
-   * @return array
-   */
-  function spyc_load_file ($file) {
-    return Spyc::YAMLLoad($file);
-  }
-}
-
-/**
-   * The Simple PHP YAML Class.
-   *
-   * This class can be used to read a YAML file and convert its contents
-   * into a PHP array.  It currently supports a very limited subsection of
-   * the YAML spec.
-   *
-   * Usage:
-   * <code>
-   *   $Spyc  = new Spyc;
-   *   $array = $Spyc->load($file);
-   * </code>
-   * or:
-   * <code>
-   *   $array = Spyc::YAMLLoad($file);
-   * </code>
-   * or:
-   * <code>
-   *   $array = spyc_load_file($file);
-   * </code>
-   * @package Spyc
-   */
-class Spyc {
-
-  // SETTINGS
-
-  const REMPTY = "\0\0\0\0\0";
-
-  /**
-   * Setting this to true will force YAMLDump to enclose any string value in
-   * quotes.  False by default.
-   * 
-   * @var bool
-   */
-  public $setting_dump_force_quotes = false;
-
-  /**
-   * Setting this to true will forse YAMLLoad to use syck_load function when
-   * possible. False by default.
-   * @var bool
-   */
-  public $setting_use_syck_is_possible = false;
-
-
-
-  /**#@+
-  * @access private
-  * @var mixed
-  */
-  private $_dumpIndent;
-  private $_dumpWordWrap;
-  private $_containsGroupAnchor = false;
-  private $_containsGroupAlias = false;
-  private $path;
-  private $result;
-  private $LiteralPlaceHolder = '___YAML_Literal_Block___';
-  private $SavedGroups = array();
-  private $indent;
-  /**
-   * Path modifier that should be applied after adding current element.
-   * @var array
-   */
-  private $delayedPath = array();
-
-  /**#@+
-  * @access public
-  * @var mixed
-  */
-  public $_nodeId;
-
-/**
- * Load a valid YAML string to Spyc.
- * @param string $input
- * @return array
- */
-  public function load ($input) {
-    return $this->__loadString($input);
-  }
-
- /**
- * Load a valid YAML file to Spyc.
- * @param string $file
- * @return array
- */
-  public function loadFile ($file) {
-    return $this->__load($file);
-  }
-
-  /**
-     * Load YAML into a PHP array statically
-     *
-     * The load method, when supplied with a YAML stream (string or file),
-     * will do its best to convert YAML in a file into a PHP array.  Pretty
-     * simple.
-     *  Usage:
-     *  <code>
-     *   $array = Spyc::YAMLLoad('lucky.yaml');
-     *   print_r($array);
-     *  </code>
-     * @access public
-     * @return array
-     * @param string $input Path of YAML file or string containing YAML
-     */
-  public static function YAMLLoad($input) {
-    $Spyc = new Spyc;
-    return $Spyc->__load($input);
-  }
-
-  /**
-     * Load a string of YAML into a PHP array statically
-     *
-     * The load method, when supplied with a YAML string, will do its best 
-     * to convert YAML in a string into a PHP array.  Pretty simple.
-     *
-     * Note: use this function if you don't want files from the file system
-     * loaded and processed as YAML.  This is of interest to people concerned
-     * about security whose input is from a string.
-     *
-     *  Usage:
-     *  <code>
-     *   $array = Spyc::YAMLLoadString("---\n0: hello world\n");
-     *   print_r($array);
-     *  </code>
-     * @access public
-     * @return array
-     * @param string $input String containing YAML
-     */
-  public static function YAMLLoadString($input) {
-    $Spyc = new Spyc;
-    return $Spyc->__loadString($input);
-  }
-
-  /**
-     * Dump YAML from PHP array statically
-     *
-     * The dump method, when supplied with an array, will do its best
-     * to convert the array into friendly YAML.  Pretty simple.  Feel free to
-     * save the returned string as nothing.yaml and pass it around.
-     *
-     * Oh, and you can decide how big the indent is and what the wordwrap
-     * for folding is.  Pretty cool -- just pass in 'false' for either if
-     * you want to use the default.
-     *
-     * Indent's default is 2 spaces, wordwrap's default is 40 characters.  And
-     * you can turn off wordwrap by passing in 0.
-     *
-     * @access public
-     * @return string
-     * @param array $array PHP array
-     * @param int $indent Pass in false to use the default, which is 2
-     * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
-     */
-  public static function YAMLDump($array,$indent = false,$wordwrap = false) {
-    $spyc = new Spyc;
-    return $spyc->dump($array,$indent,$wordwrap);
-  }
-
-
-  /**
-     * Dump PHP array to YAML
-     *
-     * The dump method, when supplied with an array, will do its best
-     * to convert the array into friendly YAML.  Pretty simple.  Feel free to
-     * save the returned string as tasteful.yaml and pass it around.
-     *
-     * Oh, and you can decide how big the indent is and what the wordwrap
-     * for folding is.  Pretty cool -- just pass in 'false' for either if
-     * you want to use the default.
-     *
-     * Indent's default is 2 spaces, wordwrap's default is 40 characters.  And
-     * you can turn off wordwrap by passing in 0.
-     *
-     * @access public
-     * @return string
-     * @param array $array PHP array
-     * @param int $indent Pass in false to use the default, which is 2
-     * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
-     */
-  public function dump($array,$indent = false,$wordwrap = false) {
-    // Dumps to some very clean YAML.  We'll have to add some more features
-    // and options soon.  And better support for folding.
-
-    // New features and options.
-    if ($indent === false or !is_numeric($indent)) {
-      $this->_dumpIndent = 2;
-    } else {
-      $this->_dumpIndent = $indent;
-    }
-
-    if ($wordwrap === false or !is_numeric($wordwrap)) {
-      $this->_dumpWordWrap = 40;
-    } else {
-      $this->_dumpWordWrap = $wordwrap;
-    }
-
-    // New YAML document
-    $string = "---\n";
-
-    // Start at the base of the array and move through it.
-    if ($array) {
-      $array = (array)$array; 
-      $previous_key = -1;
-      foreach ($array as $key => $value) {
-        if (!isset($first_key)) $first_key = $key;
-        $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
-        $previous_key = $key;
-      }
-    }
-    return $string;
-  }
-
-  /**
-     * Attempts to convert a key / value array item to YAML
-     * @access private
-     * @return string
-     * @param $key The name of the key
-     * @param $value The value of the item
-     * @param $indent The indent of the current node
-     */
-  private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
-    if (is_array($value)) {
-      if (empty ($value))
-        return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
-      // It has children.  What to do?
-      // Make it the right kind of item
-      $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
-      // Add the indent
-      $indent += $this->_dumpIndent;
-      // Yamlize the array
-      $string .= $this->_yamlizeArray($value,$indent);
-    } elseif (!is_array($value)) {
-      // It doesn't have children.  Yip.
-      $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
-    }
-    return $string;
-  }
-
-  /**
-     * Attempts to convert an array to YAML
-     * @access private
-     * @return string
-     * @param $array The array you want to convert
-     * @param $indent The indent of the current level
-     */
-  private function _yamlizeArray($array,$indent) {
-    if (is_array($array)) {
-      $string = '';
-      $previous_key = -1;
-      foreach ($array as $key => $value) {
-        if (!isset($first_key)) $first_key = $key;
-        $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
-        $previous_key = $key;
-      }
-      return $string;
-    } else {
-      return false;
-    }
-  }
-
-  /**
-     * Returns YAML from a key and a value
-     * @access private
-     * @return string
-     * @param $key The name of the key
-     * @param $value The value of the item
-     * @param $indent The indent of the current node
-     */
-  private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
-    // do some folding here, for blocks
-    if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
-      strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '  ') !== false ||
-      strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
-      substr ($value, -1, 1) == ':')
-    ) {
-      $value = $this->_doLiteralBlock($value,$indent);
-    } else {
-      $value  = $this->_doFolding($value,$indent);
-    }
-
-    if ($value === array()) $value = '[ ]';
-    if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) {
-       $value = $this->_doLiteralBlock($value,$indent);
-    }
-    if (trim ($value) != $value)
-       $value = $this->_doLiteralBlock($value,$indent);
-
-    if (is_bool($value)) {
-       $value = ($value) ? "true" : "false";
-    }
-    
-    if ($value === null) $value = 'null';
-    if ($value === "'" . self::REMPTY . "'") $value = null;
-
-    $spaces = str_repeat(' ',$indent);
-
-    //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
-    if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
-      // It's a sequence
-      $string = $spaces.'- '.$value."\n";
-    } else {
-      // if ($first_key===0)  throw new Exception('Keys are all screwy.  The first one was zero, now it\'s "'. $key .'"');
-      // It's mapped
-      if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
-      $string = rtrim ($spaces.$key.': '.$value)."\n";
-    }
-    return $string;
-  }
-
-  /**
-     * Creates a literal block for dumping
-     * @access private
-     * @return string
-     * @param $value
-     * @param $indent int The value of the indent
-     */
-  private function _doLiteralBlock($value,$indent) {
-    if ($value === "\n") return '\n';
-    if (strpos($value, "\n") === false && strpos($value, "'") === false) {
-      return sprintf ("'%s'", $value);
-    }
-    if (strpos($value, "\n") === false && strpos($value, '"') === false) {
-      return sprintf ('"%s"', $value);
-    }
-    $exploded = explode("\n",$value);
-    $newValue = '|';
-    $indent  += $this->_dumpIndent;
-    $spaces   = str_repeat(' ',$indent);
-    foreach ($exploded as $line) {
-      $newValue .= "\n" . $spaces . ($line);
-    }
-    return $newValue;
-  }
-
-  /**
-     * Folds a string of text, if necessary
-     * @access private
-     * @return string
-     * @param $value The string you wish to fold
-     */
-  private function _doFolding($value,$indent) {
-    // Don't do anything if wordwrap is set to 0
-
-    if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
-      $indent += $this->_dumpIndent;
-      $indent = str_repeat(' ',$indent);
-      $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
-      $value   = ">\n".$indent.$wrapped;
-    } else {
-      if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
-        $value = '"' . $value . '"';
-    }
-
-
-    return $value;
-  }
-
-// LOADING FUNCTIONS
-
-  private function __load($input) {
-    $Source = $this->loadFromSource($input);
-    return $this->loadWithSource($Source);
-  }
-
-  private function __loadString($input) {
-    $Source = $this->loadFromString($input);
-    return $this->loadWithSource($Source);
-  }
-
-  private function loadWithSource($Source) {
-    if (empty ($Source)) return array();
-    if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
-      $array = syck_load (implode ('', $Source));
-      return is_array($array) ? $array : array();
-    }
-
-    $this->path = array();
-    $this->result = array();
-
-    $cnt = count($Source);
-    for ($i = 0; $i < $cnt; $i++) {
-      $line = $Source[$i];
-      
-      $this->indent = strlen($line) - strlen(ltrim($line));
-      $tempPath = $this->getParentPathByIndent($this->indent);
-      $line = self::stripIndent($line, $this->indent);
-      if (self::isComment($line)) continue;
-      if (self::isEmpty($line)) continue;
-      $this->path = $tempPath;
-
-      $literalBlockStyle = self::startsLiteralBlock($line);
-      if ($literalBlockStyle) {
-        $line = rtrim ($line, $literalBlockStyle . " \n");
-        $literalBlock = '';
-        $line .= $this->LiteralPlaceHolder;
-        $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
-        while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
-          $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
-        }
-        $i--;
-      }
-
-      while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
-        $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
-      }
-      $i--;
-
-
-
-      if (strpos ($line, '#')) {
-        if (strpos ($line, '"') === false && strpos ($line, "'") === false)
-          $line = preg_replace('/\s+#(.+)$/','',$line);
-      }
-
-      $lineArray = $this->_parseLine($line);
-
-      if ($literalBlockStyle)
-        $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
-
-      $this->addArray($lineArray, $this->indent);
-
-      foreach ($this->delayedPath as $indent => $delayedPath)
-        $this->path[$indent] = $delayedPath;
-
-      $this->delayedPath = array();
-
-    }
-    return $this->result;
-  }
-
-  private function loadFromSource ($input) {
-    if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
-    return file($input);
-
-    return $this->loadFromString($input);
-  }
-
-  private function loadFromString ($input) {
-    $lines = explode("\n",$input);
-    foreach ($lines as $k => $_) {
-      $lines[$k] = rtrim ($_, "\r");
-    }
-    return $lines;
-  }
-
-  /**
-     * Parses YAML code and returns an array for a node
-     * @access private
-     * @return array
-     * @param string $line A line from the YAML file
-     */
-  private function _parseLine($line) {
-    if (!$line) return array();
-    $line = trim($line);
-    if (!$line) return array();
-
-    $array = array();
-
-    $group = $this->nodeContainsGroup($line);
-    if ($group) {
-      $this->addGroup($line, $group);
-      $line = $this->stripGroup ($line, $group);
-    }
-
-    if ($this->startsMappedSequence($line))
-      return $this->returnMappedSequence($line);
-
-    if ($this->startsMappedValue($line))
-      return $this->returnMappedValue($line);
-
-    if ($this->isArrayElement($line))
-     return $this->returnArrayElement($line);
-
-    if ($this->isPlainArray($line))
-     return $this->returnPlainArray($line); 
-     
-     
-    return $this->returnKeyValuePair($line);
-
-  }
-
-  /**
-     * Finds the type of the passed value, returns the value as the new type.
-     * @access private
-     * @param string $value
-     * @return mixed
-     */
-  private function _toType($value) {
-    if ($value === '') return null;
-    $first_character = $value[0];
-    $last_character = substr($value, -1, 1);
-
-    $is_quoted = false;
-    do {
-      if (!$value) break;
-      if ($first_character != '"' && $first_character != "'") break;
-      if ($last_character != '"' && $last_character != "'") break;
-      $is_quoted = true;
-    } while (0);
-
-    if ($is_quoted)
-      return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\''));
-    
-    if (strpos($value, ' #') !== false && !$is_quoted)
-      $value = preg_replace('/\s+#(.+)$/','',$value);
-
-    if (!$is_quoted) $value = str_replace('\n', "\n", $value);
-
-    if ($first_character == '[' && $last_character == ']') {
-      // Take out strings sequences and mappings
-      $innerValue = trim(substr ($value, 1, -1));
-      if ($innerValue === '') return array();
-      $explode = $this->_inlineEscape($innerValue);
-      // Propagate value array
-      $value  = array();
-      foreach ($explode as $v) {
-        $value[] = $this->_toType($v);
-      }
-      return $value;
-    }
-
-    if (strpos($value,': ')!==false && $first_character != '{') {
-      $array = explode(': ',$value);
-      $key   = trim($array[0]);
-      array_shift($array);
-      $value = trim(implode(': ',$array));
-      $value = $this->_toType($value);
-      return array($key => $value);
-    }
-    
-    if ($first_character == '{' && $last_character == '}') {
-      $innerValue = trim(substr ($value, 1, -1));
-      if ($innerValue === '') return array();
-      // Inline Mapping
-      // Take out strings sequences and mappings
-      $explode = $this->_inlineEscape($innerValue);
-      // Propagate value array
-      $array = array();
-      foreach ($explode as $v) {
-        $SubArr = $this->_toType($v);
-        if (empty($SubArr)) continue;
-        if (is_array ($SubArr)) {
-          $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
-        }
-        $array[] = $SubArr;
-      }
-      return $array;
-    }
-
-    if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
-      return null;
-    }
-
-    if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
-      $intvalue = (int)$value;
-      if ($intvalue != PHP_INT_MAX)
-        $value = $intvalue;
-      return $value;
-    }
-
-    if (in_array($value,
-                 array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) {
-      return true;
-    }
-
-    if (in_array(strtolower($value),
-                 array('false', 'off', '-', 'no', 'n'))) {
-      return false;
-    }
-
-    if (is_numeric($value)) {
-      if ($value === '0') return 0;
-      if (rtrim ($value, 0) === $value)
-        $value = (float)$value;
-      return $value;
-    }
-    
-    return $value;
-  }
-
-  /**
-     * Used in inlines to check for more inlines or quoted strings
-     * @access private
-     * @return array
-     */
-  private function _inlineEscape($inline) {
-    // There's gotta be a cleaner way to do this...
-    // While pure sequences seem to be nesting just fine,
-    // pure mappings and mappings with sequences inside can't go very
-    // deep.  This needs to be fixed.
-
-    $seqs = array();
-    $maps = array();
-    $saved_strings = array();
-
-    // Check for strings
-    $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
-    if (preg_match_all($regex,$inline,$strings)) {
-      $saved_strings = $strings[0];
-      $inline  = preg_replace($regex,'YAMLString',$inline);
-    }
-    unset($regex);
-
-    $i = 0;
-    do {
-
-    // Check for sequences
-    while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
-      $seqs[] = $matchseqs[0];
-      $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
-    }
-
-    // Check for mappings
-    while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
-      $maps[] = $matchmaps[0];
-      $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
-    }
-
-    if ($i++ >= 10) break;
-
-    } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
-
-    $explode = explode(', ',$inline);
-    $stringi = 0; $i = 0;
-
-    while (1) {
-
-    // Re-add the sequences
-    if (!empty($seqs)) {
-      foreach ($explode as $key => $value) {
-        if (strpos($value,'YAMLSeq') !== false) {
-          foreach ($seqs as $seqk => $seq) {
-            $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
-            $value = $explode[$key];
-          }
-        }
-      }
-    }
-
-    // Re-add the mappings
-    if (!empty($maps)) {
-      foreach ($explode as $key => $value) {
-        if (strpos($value,'YAMLMap') !== false) {
-          foreach ($maps as $mapk => $map) {
-            $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
-            $value = $explode[$key];
-          }
-        }
-      }
-    }
-
-
-    // Re-add the strings
-    if (!empty($saved_strings)) {
-      foreach ($explode as $key => $value) {
-        while (strpos($value,'YAMLString') !== false) {
-          $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
-          unset($saved_strings[$stringi]);
-          ++$stringi;
-          $value = $explode[$key];
-        }
-      }
-    }
-
-    $finished = true;
-    foreach ($explode as $key => $value) {
-      if (strpos($value,'YAMLSeq') !== false) {
-        $finished = false; break;
-      }
-      if (strpos($value,'YAMLMap') !== false) {
-        $finished = false; break;
-      }
-      if (strpos($value,'YAMLString') !== false) {
-        $finished = false; break;
-      }
-    }
-    if ($finished) break;
-
-    $i++;
-    if ($i > 10) 
-      break; // Prevent infinite loops.
-    }
-
-    return $explode;
-  }
-
-  private function literalBlockContinues ($line, $lineIndent) {
-    if (!trim($line)) return true;
-    if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
-    return false;
-  }
-
-  private function referenceContentsByAlias ($alias) {
-    do {
-      if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
-      $groupPath = $this->SavedGroups[$alias];
-      $value = $this->result;
-      foreach ($groupPath as $k) {
-        $value = $value[$k];
-      }
-    } while (false);
-    return $value;
-  }
-
-  private function addArrayInline ($array, $indent) {
-      $CommonGroupPath = $this->path;
-      if (empty ($array)) return false;
-      
-      foreach ($array as $k => $_) {
-        $this->addArray(array($k => $_), $indent);
-        $this->path = $CommonGroupPath;
-      }
-      return true;
-  }
-
-  private function addArray ($incoming_data, $incoming_indent) {
-
-   // print_r ($incoming_data);
-
-    if (count ($incoming_data) > 1)
-      return $this->addArrayInline ($incoming_data, $incoming_indent);
-    
-    $key = key ($incoming_data);
-    $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
-    if ($key === '__!YAMLZero') $key = '0';
-
-    if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
-      if ($key || $key === '' || $key === '0') {
-        $this->result[$key] = $value;
-      } else {
-        $this->result[] = $value; end ($this->result); $key = key ($this->result);
-      }
-      $this->path[$incoming_indent] = $key;
-      return;
-    }
-
-
-    
-    $history = array();
-    // Unfolding inner array tree.
-    $history[] = $_arr = $this->result;
-    foreach ($this->path as $k) {
-      $history[] = $_arr = $_arr[$k];
-    }
-
-    if ($this->_containsGroupAlias) {
-      $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
-      $this->_containsGroupAlias = false;
-    }
-
-
-    // Adding string or numeric key to the innermost level or $this->arr.
-    if (is_string($key) && $key == '<<') {
-      if (!is_array ($_arr)) { $_arr = array (); }
-
-      $_arr = array_merge ($_arr, $value);
-    } else if ($key || $key === '' || $key === '0') {
-      if (!is_array ($_arr))
-        $_arr = array ($key=>$value);
-      else
-        $_arr[$key] = $value;
-    } else {
-      if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
-      else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
-    }
-
-    $reverse_path = array_reverse($this->path);
-    $reverse_history = array_reverse ($history);
-    $reverse_history[0] = $_arr;
-    $cnt = count($reverse_history) - 1;
-    for ($i = 0; $i < $cnt; $i++) {
-      $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
-    }
-    $this->result = $reverse_history[$cnt];
-
-    $this->path[$incoming_indent] = $key;
-
-    if ($this->_containsGroupAnchor) {
-      $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
-      if (is_array ($value)) {
-        $k = key ($value);
-        if (!is_int ($k)) {
-          $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
-        }
-      }
-      $this->_containsGroupAnchor = false;
-    }
-
-  }
-
-  private static function startsLiteralBlock ($line) {
-    $lastChar = substr (trim($line), -1);
-    if ($lastChar != '>' && $lastChar != '|') return false;
-    if ($lastChar == '|') return $lastChar;
-    // HTML tags should not be counted as literal blocks.
-    if (preg_match ('#<.*?>$#', $line)) return false;
-    return $lastChar;
-  }
-
-  private static function greedilyNeedNextLine($line) {
-    $line = trim ($line);
-    if (!strlen($line)) return false;
-    if (substr ($line, -1, 1) == ']') return false;
-    if ($line[0] == '[') return true;
-    if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
-    return false;
-  }
-
-  private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
-    $line = self::stripIndent($line, $indent);
-    if ($literalBlockStyle !== '|') {
-        $line = self::stripIndent($line);
-    }
-    $line = rtrim ($line, "\r\n\t ") . "\n";
-    if ($literalBlockStyle == '|') {
-      return $literalBlock . $line;
-    }
-    if (strlen($line) == 0)
-      return rtrim($literalBlock, ' ') . "\n";
-    if ($line == "\n" && $literalBlockStyle == '>') {
-      return rtrim ($literalBlock, " \t") . "\n";
-    }
-    if ($line != "\n")
-      $line = trim ($line, "\r\n ") . " ";
-    return $literalBlock . $line;
-  }
-
-   function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
-     foreach ($lineArray as $k => $_) {
-      if (is_array($_))
-        $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
-      else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
-	       $lineArray[$k] = rtrim ($literalBlock, " \r\n");
-     }
-     return $lineArray;
-   }
-
-  private static function stripIndent ($line, $indent = -1) {
-    if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
-    return substr ($line, $indent);
-  }
-
-  private function getParentPathByIndent ($indent) {
-    if ($indent == 0) return array();
-    $linePath = $this->path;
-    do {
-      end($linePath); $lastIndentInParentPath = key($linePath);
-      if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
-    } while ($indent <= $lastIndentInParentPath);
-    return $linePath;
-  }
-
-
-  private function clearBiggerPathValues ($indent) {
-
-
-    if ($indent == 0) $this->path = array();
-    if (empty ($this->path)) return true;
-
-    foreach ($this->path as $k => $_) {
-      if ($k > $indent) unset ($this->path[$k]);
-    }
-
-    return true;
-  }
-
-
-  private static function isComment ($line) {
-    if (!$line) return false;
-    if ($line[0] == '#') return true;
-    if (trim($line, " \r\n\t") == '---') return true;
-    return false;
-  }
-
-  private static function isEmpty ($line) {
-    return (trim ($line) === '');
-  }
-
-
-  private function isArrayElement ($line) {
-    if (!$line) return false;
-    if ($line[0] != '-') return false;
-    if (strlen ($line) > 3)
-      if (substr($line,0,3) == '---') return false;
-    
-    return true;
-  }
-
-  private function isHashElement ($line) {
-    return strpos($line, ':');
-  }
-
-  private function isLiteral ($line) {
-    if ($this->isArrayElement($line)) return false;
-    if ($this->isHashElement($line)) return false;
-    return true;
-  }
-
-
-  private static function unquote ($value) {
-    if (!$value) return $value;
-    if (!is_string($value)) return $value;
-    if ($value[0] == '\'') return trim ($value, '\'');
-    if ($value[0] == '"') return trim ($value, '"');
-    return $value;
-  }
-
-  private function startsMappedSequence ($line) {
-    return ($line[0] == '-' && substr ($line, -1, 1) == ':');
-  }
-
-  private function returnMappedSequence ($line) {
-    $array = array();
-    $key         = self::unquote(trim(substr($line,1,-1)));
-    $array[$key] = array();
-    $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
-    return array($array);
-  }
-
-  private function returnMappedValue ($line) {
-    $array = array();
-    $key         = self::unquote (trim(substr($line,0,-1)));
-    $array[$key] = '';
-    return $array;
-  }
-
-  private function startsMappedValue ($line) {
-    return (substr ($line, -1, 1) == ':');
-  }
-  
-  private function isPlainArray ($line) {
-    return ($line[0] == '[' && substr ($line, -1, 1) == ']');
-  }
-  
-  private function returnPlainArray ($line) {
-    return $this->_toType($line); 
-  }  
-
-  private function returnKeyValuePair ($line) {
-    $array = array();
-    $key = '';
-    if (strpos ($line, ':')) {
-      // It's a key/value pair most likely
-      // If the key is in double quotes pull it out
-      if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
-        $value = trim(str_replace($matches[1],'',$line));
-        $key   = $matches[2];
-      } else {
-        // Do some guesswork as to the key and the value
-        $explode = explode(':',$line);
-        $key     = trim($explode[0]);
-        array_shift($explode);
-        $value   = trim(implode(':',$explode));
-      }
-      // Set the type of the value.  Int, string, etc
-      $value = $this->_toType($value);
-      if ($key === '0') $key = '__!YAMLZero';
-      $array[$key] = $value;
-    } else {
-      $array = array ($line);
-    }
-    return $array;
-
-  }
-
-
-  private function returnArrayElement ($line) {
-     if (strlen($line) <= 1) return array(array()); // Weird %)
-     $array = array();
-     $value   = trim(substr($line,1));
-     $value   = $this->_toType($value);
-     $array[] = $value;
-     return $array;
-  }
-
-
-  private function nodeContainsGroup ($line) {    
-    $symbolsForReference = 'A-z0-9_\-';
-    if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
-    if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
-    if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
-    if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
-    if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
-    if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
-    return false;
-
-  }
-
-  private function addGroup ($line, $group) {
-    if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
-    if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
-    //print_r ($this->path);
-  }
-
-  private function stripGroup ($line, $group) {
-    $line = trim(str_replace($group, '', $line));
-    return $line;
-  }
-}
-
-// Enable use of Spyc from command line
-// The syntax is the following: php spyc.php spyc.yaml
-
-define ('SPYC_FROM_COMMAND_LINE', false);
-
-do {
-  if (!SPYC_FROM_COMMAND_LINE) break;
-  if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
-  if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break;
-  $file = $argv[1];
-  printf ("Spyc loading file: %s\n", $file);
-  print_r (spyc_load_file ($file));
+<?php
+/**
+   * Spyc -- A Simple PHP YAML Class
+   * @version 0.5
+   * @author Vlad Andersen <vlad.andersen@gmail.com>
+   * @author Chris Wanstrath <chris@ozmm.org>
+   * @link http://code.google.com/p/spyc/
+   * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
+   * @license http://www.opensource.org/licenses/mit-license.php MIT License
+   * @package Spyc
+   */
+
+if (!function_exists('spyc_load')) {
+  /**
+   * Parses YAML to array.
+   * @param string $string YAML string.
+   * @return array
+   */
+  function spyc_load ($string) {
+    return Spyc::YAMLLoadString($string);
+  }
+}
+
+if (!function_exists('spyc_load_file')) {
+  /**
+   * Parses YAML to array.
+   * @param string $file Path to YAML file.
+   * @return array
+   */
+  function spyc_load_file ($file) {
+    return Spyc::YAMLLoad($file);
+  }
+}
+
+/**
+   * The Simple PHP YAML Class.
+   *
+   * This class can be used to read a YAML file and convert its contents
+   * into a PHP array.  It currently supports a very limited subsection of
+   * the YAML spec.
+   *
+   * Usage:
+   * <code>
+   *   $Spyc  = new Spyc;
+   *   $array = $Spyc->load($file);
+   * </code>
+   * or:
+   * <code>
+   *   $array = Spyc::YAMLLoad($file);
+   * </code>
+   * or:
+   * <code>
+   *   $array = spyc_load_file($file);
+   * </code>
+   * @package Spyc
+   */
+class Spyc {
+
+  // SETTINGS
+
+  const REMPTY = "\0\0\0\0\0";
+
+  /**
+   * Setting this to true will force YAMLDump to enclose any string value in
+   * quotes.  False by default.
+   * 
+   * @var bool
+   */
+  public $setting_dump_force_quotes = false;
+
+  /**
+   * Setting this to true will forse YAMLLoad to use syck_load function when
+   * possible. False by default.
+   * @var bool
+   */
+  public $setting_use_syck_is_possible = false;
+
+
+
+  /**#@+
+  * @access private
+  * @var mixed
+  */
+  private $_dumpIndent;
+  private $_dumpWordWrap;
+  private $_containsGroupAnchor = false;
+  private $_containsGroupAlias = false;
+  private $path;
+  private $result;
+  private $LiteralPlaceHolder = '___YAML_Literal_Block___';
+  private $SavedGroups = array();
+  private $indent;
+  /**
+   * Path modifier that should be applied after adding current element.
+   * @var array
+   */
+  private $delayedPath = array();
+
+  /**#@+
+  * @access public
+  * @var mixed
+  */
+  public $_nodeId;
+
+/**
+ * Load a valid YAML string to Spyc.
+ * @param string $input
+ * @return array
+ */
+  public function load ($input) {
+    return $this->__loadString($input);
+  }
+
+ /**
+ * Load a valid YAML file to Spyc.
+ * @param string $file
+ * @return array
+ */
+  public function loadFile ($file) {
+    return $this->__load($file);
+  }
+
+  /**
+     * Load YAML into a PHP array statically
+     *
+     * The load method, when supplied with a YAML stream (string or file),
+     * will do its best to convert YAML in a file into a PHP array.  Pretty
+     * simple.
+     *  Usage:
+     *  <code>
+     *   $array = Spyc::YAMLLoad('lucky.yaml');
+     *   print_r($array);
+     *  </code>
+     * @access public
+     * @return array
+     * @param string $input Path of YAML file or string containing YAML
+     */
+  public static function YAMLLoad($input) {
+    $Spyc = new Spyc;
+    return $Spyc->__load($input);
+  }
+
+  /**
+     * Load a string of YAML into a PHP array statically
+     *
+     * The load method, when supplied with a YAML string, will do its best 
+     * to convert YAML in a string into a PHP array.  Pretty simple.
+     *
+     * Note: use this function if you don't want files from the file system
+     * loaded and processed as YAML.  This is of interest to people concerned
+     * about security whose input is from a string.
+     *
+     *  Usage:
+     *  <code>
+     *   $array = Spyc::YAMLLoadString("---\n0: hello world\n");
+     *   print_r($array);
+     *  </code>
+     * @access public
+     * @return array
+     * @param string $input String containing YAML
+     */
+  public static function YAMLLoadString($input) {
+    $Spyc = new Spyc;
+    return $Spyc->__loadString($input);
+  }
+
+  /**
+     * Dump YAML from PHP array statically
+     *
+     * The dump method, when supplied with an array, will do its best
+     * to convert the array into friendly YAML.  Pretty simple.  Feel free to
+     * save the returned string as nothing.yaml and pass it around.
+     *
+     * Oh, and you can decide how big the indent is and what the wordwrap
+     * for folding is.  Pretty cool -- just pass in 'false' for either if
+     * you want to use the default.
+     *
+     * Indent's default is 2 spaces, wordwrap's default is 40 characters.  And
+     * you can turn off wordwrap by passing in 0.
+     *
+     * @access public
+     * @return string
+     * @param array $array PHP array
+     * @param int $indent Pass in false to use the default, which is 2
+     * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
+     */
+  public static function YAMLDump($array,$indent = false,$wordwrap = false) {
+    $spyc = new Spyc;
+    return $spyc->dump($array,$indent,$wordwrap);
+  }
+
+
+  /**
+     * Dump PHP array to YAML
+     *
+     * The dump method, when supplied with an array, will do its best
+     * to convert the array into friendly YAML.  Pretty simple.  Feel free to
+     * save the returned string as tasteful.yaml and pass it around.
+     *
+     * Oh, and you can decide how big the indent is and what the wordwrap
+     * for folding is.  Pretty cool -- just pass in 'false' for either if
+     * you want to use the default.
+     *
+     * Indent's default is 2 spaces, wordwrap's default is 40 characters.  And
+     * you can turn off wordwrap by passing in 0.
+     *
+     * @access public
+     * @return string
+     * @param array $array PHP array
+     * @param int $indent Pass in false to use the default, which is 2
+     * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
+     */
+  public function dump($array,$indent = false,$wordwrap = false) {
+    // Dumps to some very clean YAML.  We'll have to add some more features
+    // and options soon.  And better support for folding.
+
+    // New features and options.
+    if ($indent === false or !is_numeric($indent)) {
+      $this->_dumpIndent = 2;
+    } else {
+      $this->_dumpIndent = $indent;
+    }
+
+    if ($wordwrap === false or !is_numeric($wordwrap)) {
+      $this->_dumpWordWrap = 40;
+    } else {
+      $this->_dumpWordWrap = $wordwrap;
+    }
+
+    // New YAML document
+    $string = "---\n";
+
+    // Start at the base of the array and move through it.
+    if ($array) {
+      $array = (array)$array; 
+      $previous_key = -1;
+      foreach ($array as $key => $value) {
+        if (!isset($first_key)) $first_key = $key;
+        $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
+        $previous_key = $key;
+      }
+    }
+    return $string;
+  }
+
+  /**
+     * Attempts to convert a key / value array item to YAML
+     * @access private
+     * @return string
+     * @param $key The name of the key
+     * @param $value The value of the item
+     * @param $indent The indent of the current node
+     */
+  private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
+    if (is_array($value)) {
+      if (empty ($value))
+        return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
+      // It has children.  What to do?
+      // Make it the right kind of item
+      $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
+      // Add the indent
+      $indent += $this->_dumpIndent;
+      // Yamlize the array
+      $string .= $this->_yamlizeArray($value,$indent);
+    } elseif (!is_array($value)) {
+      // It doesn't have children.  Yip.
+      $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
+    }
+    return $string;
+  }
+
+  /**
+     * Attempts to convert an array to YAML
+     * @access private
+     * @return string
+     * @param $array The array you want to convert
+     * @param $indent The indent of the current level
+     */
+  private function _yamlizeArray($array,$indent) {
+    if (is_array($array)) {
+      $string = '';
+      $previous_key = -1;
+      foreach ($array as $key => $value) {
+        if (!isset($first_key)) $first_key = $key;
+        $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
+        $previous_key = $key;
+      }
+      return $string;
+    } else {
+      return false;
+    }
+  }
+
+  /**
+     * Returns YAML from a key and a value
+     * @access private
+     * @return string
+     * @param $key The name of the key
+     * @param $value The value of the item
+     * @param $indent The indent of the current node
+     */
+  private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
+    // do some folding here, for blocks
+    if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
+      strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '  ') !== false ||
+      strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
+      substr ($value, -1, 1) == ':')
+    ) {
+      $value = $this->_doLiteralBlock($value,$indent);
+    } else {
+      $value  = $this->_doFolding($value,$indent);
+    }
+
+    if ($value === array()) $value = '[ ]';
+    if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) {
+       $value = $this->_doLiteralBlock($value,$indent);
+    }
+    if (trim ($value) != $value)
+       $value = $this->_doLiteralBlock($value,$indent);
+
+    if (is_bool($value)) {
+       $value = ($value) ? "true" : "false";
+    }
+    
+    if ($value === null) $value = 'null';
+    if ($value === "'" . self::REMPTY . "'") $value = null;
+
+    $spaces = str_repeat(' ',$indent);
+
+    //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
+    if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
+      // It's a sequence
+      $string = $spaces.'- '.$value."\n";
+    } else {
+      // if ($first_key===0)  throw new Exception('Keys are all screwy.  The first one was zero, now it\'s "'. $key .'"');
+      // It's mapped
+      if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
+      $string = rtrim ($spaces.$key.': '.$value)."\n";
+    }
+    return $string;
+  }
+
+  /**
+     * Creates a literal block for dumping
+     * @access private
+     * @return string
+     * @param $value
+     * @param $indent int The value of the indent
+     */
+  private function _doLiteralBlock($value,$indent) {
+    if ($value === "\n") return '\n';
+    if (strpos($value, "\n") === false && strpos($value, "'") === false) {
+      return sprintf ("'%s'", $value);
+    }
+    if (strpos($value, "\n") === false && strpos($value, '"') === false) {
+      return sprintf ('"%s"', $value);
+    }
+    $exploded = explode("\n",$value);
+    $newValue = '|';
+    $indent  += $this->_dumpIndent;
+    $spaces   = str_repeat(' ',$indent);
+    foreach ($exploded as $line) {
+      $newValue .= "\n" . $spaces . ($line);
+    }
+    return $newValue;
+  }
+
+  /**
+     * Folds a string of text, if necessary
+     * @access private
+     * @return string
+     * @param $value The string you wish to fold
+     */
+  private function _doFolding($value,$indent) {
+    // Don't do anything if wordwrap is set to 0
+
+    if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
+      $indent += $this->_dumpIndent;
+      $indent = str_repeat(' ',$indent);
+      $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
+      $value   = ">\n".$indent.$wrapped;
+    } else {
+      if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
+        $value = '"' . $value . '"';
+    }
+
+
+    return $value;
+  }
+
+// LOADING FUNCTIONS
+
+  private function __load($input) {
+    $Source = $this->loadFromSource($input);
+    return $this->loadWithSource($Source);
+  }
+
+  private function __loadString($input) {
+    $Source = $this->loadFromString($input);
+    return $this->loadWithSource($Source);
+  }
+
+  private function loadWithSource($Source) {
+    if (empty ($Source)) return array();
+    if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
+      $array = syck_load (implode ('', $Source));
+      return is_array($array) ? $array : array();
+    }
+
+    $this->path = array();
+    $this->result = array();
+
+    $cnt = count($Source);
+    for ($i = 0; $i < $cnt; $i++) {
+      $line = $Source[$i];
+      
+      $this->indent = strlen($line) - strlen(ltrim($line));
+      $tempPath = $this->getParentPathByIndent($this->indent);
+      $line = self::stripIndent($line, $this->indent);
+      if (self::isComment($line)) continue;
+      if (self::isEmpty($line)) continue;
+      $this->path = $tempPath;
+
+      $literalBlockStyle = self::startsLiteralBlock($line);
+      if ($literalBlockStyle) {
+        $line = rtrim ($line, $literalBlockStyle . " \n");
+        $literalBlock = '';
+        $line .= $this->LiteralPlaceHolder;
+        $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
+        while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
+          $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
+        }
+        $i--;
+      }
+
+      while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
+        $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
+      }
+      $i--;
+
+
+
+      if (strpos ($line, '#')) {
+        if (strpos ($line, '"') === false && strpos ($line, "'") === false)
+          $line = preg_replace('/\s+#(.+)$/','',$line);
+      }
+
+      $lineArray = $this->_parseLine($line);
+
+      if ($literalBlockStyle)
+        $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
+
+      $this->addArray($lineArray, $this->indent);
+
+      foreach ($this->delayedPath as $indent => $delayedPath)
+        $this->path[$indent] = $delayedPath;
+
+      $this->delayedPath = array();
+
+    }
+    return $this->result;
+  }
+
+  private function loadFromSource ($input) {
+    if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
+    return file($input);
+
+    return $this->loadFromString($input);
+  }
+
+  private function loadFromString ($input) {
+    $lines = explode("\n",$input);
+    foreach ($lines as $k => $_) {
+      $lines[$k] = rtrim ($_, "\r");
+    }
+    return $lines;
+  }
+
+  /**
+     * Parses YAML code and returns an array for a node
+     * @access private
+     * @return array
+     * @param string $line A line from the YAML file
+     */
+  private function _parseLine($line) {
+    if (!$line) return array();
+    $line = trim($line);
+    if (!$line) return array();
+
+    $array = array();
+
+    $group = $this->nodeContainsGroup($line);
+    if ($group) {
+      $this->addGroup($line, $group);
+      $line = $this->stripGroup ($line, $group);
+    }
+
+    if ($this->startsMappedSequence($line))
+      return $this->returnMappedSequence($line);
+
+    if ($this->startsMappedValue($line))
+      return $this->returnMappedValue($line);
+
+    if ($this->isArrayElement($line))
+     return $this->returnArrayElement($line);
+
+    if ($this->isPlainArray($line))
+     return $this->returnPlainArray($line); 
+     
+     
+    return $this->returnKeyValuePair($line);
+
+  }
+
+  /**
+     * Finds the type of the passed value, returns the value as the new type.
+     * @access private
+     * @param string $value
+     * @return mixed
+     */
+  private function _toType($value) {
+    if ($value === '') return null;
+    $first_character = $value[0];
+    $last_character = substr($value, -1, 1);
+
+    $is_quoted = false;
+    do {
+      if (!$value) break;
+      if ($first_character != '"' && $first_character != "'") break;
+      if ($last_character != '"' && $last_character != "'") break;
+      $is_quoted = true;
+    } while (0);
+
+    if ($is_quoted)
+      return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\''));
+    
+    if (strpos($value, ' #') !== false && !$is_quoted)
+      $value = preg_replace('/\s+#(.+)$/','',$value);
+
+    if (!$is_quoted) $value = str_replace('\n', "\n", $value);
+
+    if ($first_character == '[' && $last_character == ']') {
+      // Take out strings sequences and mappings
+      $innerValue = trim(substr ($value, 1, -1));
+      if ($innerValue === '') return array();
+      $explode = $this->_inlineEscape($innerValue);
+      // Propagate value array
+      $value  = array();
+      foreach ($explode as $v) {
+        $value[] = $this->_toType($v);
+      }
+      return $value;
+    }
+
+    if (strpos($value,': ')!==false && $first_character != '{') {
+      $array = explode(': ',$value);
+      $key   = trim($array[0]);
+      array_shift($array);
+      $value = trim(implode(': ',$array));
+      $value = $this->_toType($value);
+      return array($key => $value);
+    }
+    
+    if ($first_character == '{' && $last_character == '}') {
+      $innerValue = trim(substr ($value, 1, -1));
+      if ($innerValue === '') return array();
+      // Inline Mapping
+      // Take out strings sequences and mappings
+      $explode = $this->_inlineEscape($innerValue);
+      // Propagate value array
+      $array = array();
+      foreach ($explode as $v) {
+        $SubArr = $this->_toType($v);
+        if (empty($SubArr)) continue;
+        if (is_array ($SubArr)) {
+          $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
+        }
+        $array[] = $SubArr;
+      }
+      return $array;
+    }
+
+    if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
+      return null;
+    }
+
+    if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
+      $intvalue = (int)$value;
+      if ($intvalue != PHP_INT_MAX)
+        $value = $intvalue;
+      return $value;
+    }
+
+    if (in_array($value,
+                 array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) {
+      return true;
+    }
+
+    if (in_array(strtolower($value),
+                 array('false', 'off', '-', 'no', 'n'))) {
+      return false;
+    }
+
+    if (is_numeric($value)) {
+      if ($value === '0') return 0;
+      if (rtrim ($value, 0) === $value)
+        $value = (float)$value;
+      return $value;
+    }
+    
+    return $value;
+  }
+
+  /**
+     * Used in inlines to check for more inlines or quoted strings
+     * @access private
+     * @return array
+     */
+  private function _inlineEscape($inline) {
+    // There's gotta be a cleaner way to do this...
+    // While pure sequences seem to be nesting just fine,
+    // pure mappings and mappings with sequences inside can't go very
+    // deep.  This needs to be fixed.
+
+    $seqs = array();
+    $maps = array();
+    $saved_strings = array();
+
+    // Check for strings
+    $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
+    if (preg_match_all($regex,$inline,$strings)) {
+      $saved_strings = $strings[0];
+      $inline  = preg_replace($regex,'YAMLString',$inline);
+    }
+    unset($regex);
+
+    $i = 0;
+    do {
+
+    // Check for sequences
+    while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
+      $seqs[] = $matchseqs[0];
+      $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
+    }
+
+    // Check for mappings
+    while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
+      $maps[] = $matchmaps[0];
+      $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
+    }
+
+    if ($i++ >= 10) break;
+
+    } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
+
+    $explode = explode(', ',$inline);
+    $stringi = 0; $i = 0;
+
+    while (1) {
+
+    // Re-add the sequences
+    if (!empty($seqs)) {
+      foreach ($explode as $key => $value) {
+        if (strpos($value,'YAMLSeq') !== false) {
+          foreach ($seqs as $seqk => $seq) {
+            $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
+            $value = $explode[$key];
+          }
+        }
+      }
+    }
+
+    // Re-add the mappings
+    if (!empty($maps)) {
+      foreach ($explode as $key => $value) {
+        if (strpos($value,'YAMLMap') !== false) {
+          foreach ($maps as $mapk => $map) {
+            $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
+            $value = $explode[$key];
+          }
+        }
+      }
+    }
+
+
+    // Re-add the strings
+    if (!empty($saved_strings)) {
+      foreach ($explode as $key => $value) {
+        while (strpos($value,'YAMLString') !== false) {
+          $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
+          unset($saved_strings[$stringi]);
+          ++$stringi;
+          $value = $explode[$key];
+        }
+      }
+    }
+
+    $finished = true;
+    foreach ($explode as $key => $value) {
+      if (strpos($value,'YAMLSeq') !== false) {
+        $finished = false; break;
+      }
+      if (strpos($value,'YAMLMap') !== false) {
+        $finished = false; break;
+      }
+      if (strpos($value,'YAMLString') !== false) {
+        $finished = false; break;
+      }
+    }
+    if ($finished) break;
+
+    $i++;
+    if ($i > 10) 
+      break; // Prevent infinite loops.
+    }
+
+    return $explode;
+  }
+
+  private function literalBlockContinues ($line, $lineIndent) {
+    if (!trim($line)) return true;
+    if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
+    return false;
+  }
+
+  private function referenceContentsByAlias ($alias) {
+    do {
+      if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
+      $groupPath = $this->SavedGroups[$alias];
+      $value = $this->result;
+      foreach ($groupPath as $k) {
+        $value = $value[$k];
+      }
+    } while (false);
+    return $value;
+  }
+
+  private function addArrayInline ($array, $indent) {
+      $CommonGroupPath = $this->path;
+      if (empty ($array)) return false;
+      
+      foreach ($array as $k => $_) {
+        $this->addArray(array($k => $_), $indent);
+        $this->path = $CommonGroupPath;
+      }
+      return true;
+  }
+
+  private function addArray ($incoming_data, $incoming_indent) {
+
+   // print_r ($incoming_data);
+
+    if (count ($incoming_data) > 1)
+      return $this->addArrayInline ($incoming_data, $incoming_indent);
+    
+    $key = key ($incoming_data);
+    $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
+    if ($key === '__!YAMLZero') $key = '0';
+
+    if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
+      if ($key || $key === '' || $key === '0') {
+        $this->result[$key] = $value;
+      } else {
+        $this->result[] = $value; end ($this->result); $key = key ($this->result);
+      }
+      $this->path[$incoming_indent] = $key;
+      return;
+    }
+
+
+    
+    $history = array();
+    // Unfolding inner array tree.
+    $history[] = $_arr = $this->result;
+    foreach ($this->path as $k) {
+      $history[] = $_arr = $_arr[$k];
+    }
+
+    if ($this->_containsGroupAlias) {
+      $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
+      $this->_containsGroupAlias = false;
+    }
+
+
+    // Adding string or numeric key to the innermost level or $this->arr.
+    if (is_string($key) && $key == '<<') {
+      if (!is_array ($_arr)) { $_arr = array (); }
+
+      $_arr = array_merge ($_arr, $value);
+    } else if ($key || $key === '' || $key === '0') {
+      if (!is_array ($_arr))
+        $_arr = array ($key=>$value);
+      else
+        $_arr[$key] = $value;
+    } else {
+      if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
+      else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
+    }
+
+    $reverse_path = array_reverse($this->path);
+    $reverse_history = array_reverse ($history);
+    $reverse_history[0] = $_arr;
+    $cnt = count($reverse_history) - 1;
+    for ($i = 0; $i < $cnt; $i++) {
+      $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
+    }
+    $this->result = $reverse_history[$cnt];
+
+    $this->path[$incoming_indent] = $key;
+
+    if ($this->_containsGroupAnchor) {
+      $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
+      if (is_array ($value)) {
+        $k = key ($value);
+        if (!is_int ($k)) {
+          $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
+        }
+      }
+      $this->_containsGroupAnchor = false;
+    }
+
+  }
+
+  private static function startsLiteralBlock ($line) {
+    $lastChar = substr (trim($line), -1);
+    if ($lastChar != '>' && $lastChar != '|') return false;
+    if ($lastChar == '|') return $lastChar;
+    // HTML tags should not be counted as literal blocks.
+    if (preg_match ('#<.*?>$#', $line)) return false;
+    return $lastChar;
+  }
+
+  private static function greedilyNeedNextLine($line) {
+    $line = trim ($line);
+    if (!strlen($line)) return false;
+    if (substr ($line, -1, 1) == ']') return false;
+    if ($line[0] == '[') return true;
+    if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
+    return false;
+  }
+
+  private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
+    $line = self::stripIndent($line, $indent);
+    if ($literalBlockStyle !== '|') {
+        $line = self::stripIndent($line);
+    }
+    $line = rtrim ($line, "\r\n\t ") . "\n";
+    if ($literalBlockStyle == '|') {
+      return $literalBlock . $line;
+    }
+    if (strlen($line) == 0)
+      return rtrim($literalBlock, ' ') . "\n";
+    if ($line == "\n" && $literalBlockStyle == '>') {
+      return rtrim ($literalBlock, " \t") . "\n";
+    }
+    if ($line != "\n")
+      $line = trim ($line, "\r\n ") . " ";
+    return $literalBlock . $line;
+  }
+
+   function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
+     foreach ($lineArray as $k => $_) {
+      if (is_array($_))
+        $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
+      else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
+	       $lineArray[$k] = rtrim ($literalBlock, " \r\n");
+     }
+     return $lineArray;
+   }
+
+  private static function stripIndent ($line, $indent = -1) {
+    if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
+    return substr ($line, $indent);
+  }
+
+  private function getParentPathByIndent ($indent) {
+    if ($indent == 0) return array();
+    $linePath = $this->path;
+    do {
+      end($linePath); $lastIndentInParentPath = key($linePath);
+      if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
+    } while ($indent <= $lastIndentInParentPath);
+    return $linePath;
+  }
+
+
+  private function clearBiggerPathValues ($indent) {
+
+
+    if ($indent == 0) $this->path = array();
+    if (empty ($this->path)) return true;
+
+    foreach ($this->path as $k => $_) {
+      if ($k > $indent) unset ($this->path[$k]);
+    }
+
+    return true;
+  }
+
+
+  private static function isComment ($line) {
+    if (!$line) return false;
+    if ($line[0] == '#') return true;
+    if (trim($line, " \r\n\t") == '---') return true;
+    return false;
+  }
+
+  private static function isEmpty ($line) {
+    return (trim ($line) === '');
+  }
+
+
+  private function isArrayElement ($line) {
+    if (!$line) return false;
+    if ($line[0] != '-') return false;
+    if (strlen ($line) > 3)
+      if (substr($line,0,3) == '---') return false;
+    
+    return true;
+  }
+
+  private function isHashElement ($line) {
+    return strpos($line, ':');
+  }
+
+  private function isLiteral ($line) {
+    if ($this->isArrayElement($line)) return false;
+    if ($this->isHashElement($line)) return false;
+    return true;
+  }
+
+
+  private static function unquote ($value) {
+    if (!$value) return $value;
+    if (!is_string($value)) return $value;
+    if ($value[0] == '\'') return trim ($value, '\'');
+    if ($value[0] == '"') return trim ($value, '"');
+    return $value;
+  }
+
+  private function startsMappedSequence ($line) {
+    return ($line[0] == '-' && substr ($line, -1, 1) == ':');
+  }
+
+  private function returnMappedSequence ($line) {
+    $array = array();
+    $key         = self::unquote(trim(substr($line,1,-1)));
+    $array[$key] = array();
+    $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
+    return array($array);
+  }
+
+  private function returnMappedValue ($line) {
+    $array = array();
+    $key         = self::unquote (trim(substr($line,0,-1)));
+    $array[$key] = '';
+    return $array;
+  }
+
+  private function startsMappedValue ($line) {
+    return (substr ($line, -1, 1) == ':');
+  }
+  
+  private function isPlainArray ($line) {
+    return ($line[0] == '[' && substr ($line, -1, 1) == ']');
+  }
+  
+  private function returnPlainArray ($line) {
+    return $this->_toType($line); 
+  }  
+
+  private function returnKeyValuePair ($line) {
+    $array = array();
+    $key = '';
+    if (strpos ($line, ':')) {
+      // It's a key/value pair most likely
+      // If the key is in double quotes pull it out
+      if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
+        $value = trim(str_replace($matches[1],'',$line));
+        $key   = $matches[2];
+      } else {
+        // Do some guesswork as to the key and the value
+        $explode = explode(':',$line);
+        $key     = trim($explode[0]);
+        array_shift($explode);
+        $value   = trim(implode(':',$explode));
+      }
+      // Set the type of the value.  Int, string, etc
+      $value = $this->_toType($value);
+      if ($key === '0') $key = '__!YAMLZero';
+      $array[$key] = $value;
+    } else {
+      $array = array ($line);
+    }
+    return $array;
+
+  }
+
+
+  private function returnArrayElement ($line) {
+     if (strlen($line) <= 1) return array(array()); // Weird %)
+     $array = array();
+     $value   = trim(substr($line,1));
+     $value   = $this->_toType($value);
+     $array[] = $value;
+     return $array;
+  }
+
+
+  private function nodeContainsGroup ($line) {    
+    $symbolsForReference = 'A-z0-9_\-';
+    if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
+    if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
+    if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
+    if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
+    if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
+    if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
+    return false;
+
+  }
+
+  private function addGroup ($line, $group) {
+    if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
+    if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
+    //print_r ($this->path);
+  }
+
+  private function stripGroup ($line, $group) {
+    $line = trim(str_replace($group, '', $line));
+    return $line;
+  }
+}
+
+// Enable use of Spyc from command line
+// The syntax is the following: php spyc.php spyc.yaml
+
+define ('SPYC_FROM_COMMAND_LINE', false);
+
+do {
+  if (!SPYC_FROM_COMMAND_LINE) break;
+  if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
+  if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break;
+  $file = $argv[1];
+  printf ("Spyc loading file: %s\n", $file);
+  print_r (spyc_load_file ($file));
 } while (0);
\ No newline at end of file
diff --git a/plugins/DevicesDetection/functions.php b/plugins/DevicesDetection/functions.php
index 34f8ed8690..0812741aec 100644
--- a/plugins/DevicesDetection/functions.php
+++ b/plugins/DevicesDetection/functions.php
@@ -130,18 +130,16 @@ function Piwik_getOsFullNameExtended($label)
     if (!empty($label) && $label != ";") {
         $os = substr($label, 0, 3);
         $ver = substr($label, 4, 15);
-        $osFullName = array_search($os, UserAgentParserEnhanced::$osShorts);
-        if ($osFullName) {
-            if (in_array($os, UserAgentParserEnhanced::$osFamilies['Windows'])) {
-                return $osFullName;
-            } else {
-                return trim($osFullName . " " . $ver);
-            }
+        $name = UserAgentParserEnhanced::getOsNameFromId($os, $ver);
+        if(!empty($name)) {
+            return $name;
         }
     }
     return Piwik_Translate('General_Unknown');
 }
 
+
+
 function Piwik_getOsLogoExtended($label)
 {
     $short = substr($label, 0, 3);
diff --git a/plugins/DevicesDetection/lang/en.php b/plugins/DevicesDetection/lang/en.php
index 3df87d9fd5..d6470a95fe 100644
--- a/plugins/DevicesDetection/lang/en.php
+++ b/plugins/DevicesDetection/lang/en.php
@@ -1,7 +1,7 @@
 <?php
 
 $translations = array(
-    "DevicesDetection_description" => "Plugin providing extended information about mobile devices visiting page. Also new, more specific reports are available.",
+    "DevicesDetection_description" => "This plugin provides extended information about mobile devices, such as Brand (manufacturer), Model (device version), better Device type detection (tv, consoles, smart phones, desktop, etc) and more. This plugin adds a new report in 'Visitors > Devices'.",
     "DevicesDetection_submenu" => "Devices",
     'DevicesDetection_DevicesDetection' => "Visitor devices",
     // DataTable label translations for reports
diff --git a/tests/LocalTracker.php b/tests/LocalTracker.php
index 961fa17fe3..52d46b4c2b 100755
--- a/tests/LocalTracker.php
+++ b/tests/LocalTracker.php
@@ -47,6 +47,7 @@ class Piwik_LocalTracker extends PiwikTracker
 
         // save some values
         $plugins = Piwik_Config::getInstance()->Plugins['Plugins'];
+        $plugins[] = 'DevicesDetection';
         $pluginsTracker = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
         $oldTrackerConfig = Piwik_Config::getInstance()->Tracker;
 
diff --git a/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php b/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
index 7c2c0e598c..f5861b94ca 100644
--- a/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
+++ b/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
@@ -70,9 +70,7 @@ class WidgetsListTest extends DatabaseTestCase
 
         $_GET['idSite'] = 1;
 
-        $pluginsManager = Piwik_PluginsManager::getInstance();
-        $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
-        $pluginsManager->loadPlugins($pluginsToLoad);
+        IntegrationTestCase::loadAllPlugins();
 
         Piwik_WidgetsList::_reset();
         $widgets = Piwik_GetWidgetsList();
@@ -109,9 +107,7 @@ class WidgetsListTest extends DatabaseTestCase
 
         $_GET['idSite'] = 1;
 
-        $pluginsManager = Piwik_PluginsManager::getInstance();
-        $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
-        $pluginsManager->loadPlugins($pluginsToLoad);
+        IntegrationTestCase::loadAllPlugins();
 
         Piwik_WidgetsList::_reset();
         $widgets = Piwik_GetWidgetsList();
diff --git a/tests/PHPUnit/Core/SegmentTest.php b/tests/PHPUnit/Core/SegmentTest.php
index c007cc9d20..2a00a0a9b9 100644
--- a/tests/PHPUnit/Core/SegmentTest.php
+++ b/tests/PHPUnit/Core/SegmentTest.php
@@ -17,14 +17,13 @@ class SegmentTest extends PHPUnit_Framework_TestCase
         Zend_Registry::set('access', $pseudoMockAccess);
 
         // Load and install plugins
-        $pluginsManager = Piwik_PluginsManager::getInstance();
-        $pluginsManager->loadPlugins(Piwik_Config::getInstance()->Plugins['Plugins']);
+        IntegrationTestCase::loadAllPlugins();
     }
 
     public function tearDown()
     {
         parent::tearDown();
-        Piwik_PluginsManager::getInstance()->unloadPlugins();
+        IntegrationTestCase::unloadAllPlugins();
     }
 
     protected function _filterWhitsSpaces($valueToFilter)
diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php
index 5dae1def65..a1a5c9948d 100755
--- a/tests/PHPUnit/IntegrationTestCase.php
+++ b/tests/PHPUnit/IntegrationTestCase.php
@@ -69,11 +69,11 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
         }
     }
 
-
     public static function loadAllPlugins()
     {
         $pluginsManager = Piwik_PluginsManager::getInstance();
         $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
+        $pluginsToLoad[] = 'DevicesDetection';
         $pluginsManager->loadPlugins($pluginsToLoad);
     }
 
diff --git a/tests/PHPUnit/proxy/piwik.php b/tests/PHPUnit/proxy/piwik.php
index f70183ed16..0be50af23b 100755
--- a/tests/PHPUnit/proxy/piwik.php
+++ b/tests/PHPUnit/proxy/piwik.php
@@ -25,6 +25,13 @@ require_once PIWIK_INCLUDE_PATH . '/core/Loader.php';
 Piwik::createConfigObject();
 Piwik_Config::getInstance()->setTestEnvironment();
 Piwik_Config::getInstance()->PluginsInstalled['PluginsInstalled'] = array();
+try {
+    $trackerPlugins = Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'];
+}catch(Exception $e) {
+    $trackerPlugins = array();
+}
+$trackerPlugins[] = 'DevicesDetection';
+Piwik_Config::getInstance()->Plugins_Tracker['Plugins_Tracker'] = $trackerPlugins;
 Piwik_UserCountry_LocationProvider_GeoIp::$geoIPDatabaseDir = 'tests/lib/geoip-files';
 
 Piwik_Tracker::setTestEnvironment();
-- 
GitLab