From 58c88c1b2980ee1c02a47cb3d35de1480402e6ac Mon Sep 17 00:00:00 2001 From: mattab Date: Fri, 24 May 2013 23:23:17 +1200 Subject: Refs #3612 * making tests pass! * enabling DevicesDetection tests, even though DevicesDetection plugin is disabled by default. --- plugins/DevicesDetection/DevicesDetection.php | 4 +- .../UserAgentParserEnhanced.php | 1455 +++++++------- .../UserAgentParserEnhanced/regexes/browsers.yml | 814 ++++---- .../UserAgentParserEnhanced/regexes/mobiles.yml | 1914 +++++++++--------- .../UserAgentParserEnhanced/regexes/oss.yml | 852 ++++---- .../UserAgentParserEnhanced/spyc.php | 2090 ++++++++++---------- plugins/DevicesDetection/functions.php | 12 +- plugins/DevicesDetection/lang/en.php | 2 +- 8 files changed, 3577 insertions(+), 3566 deletions(-) (limited to 'plugins') 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 @@ - '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'; - } - + '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|(? - * @author Chris Wanstrath - * @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: - * - * $Spyc = new Spyc; - * $array = $Spyc->load($file); - * - * or: - * - * $array = Spyc::YAMLLoad($file); - * - * or: - * - * $array = spyc_load_file($file); - * - * @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: - * - * $array = Spyc::YAMLLoad('lucky.yaml'); - * print_r($array); - * - * @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: - * - * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); - * print_r($array); - * - * @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)); + + * @author Chris Wanstrath + * @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: + * + * $Spyc = new Spyc; + * $array = $Spyc->load($file); + * + * or: + * + * $array = Spyc::YAMLLoad($file); + * + * or: + * + * $array = spyc_load_file($file); + * + * @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: + * + * $array = Spyc::YAMLLoad('lucky.yaml'); + * print_r($array); + * + * @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: + * + * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); + * print_r($array); + * + * @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 @@ "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 -- cgit v1.2.3