PluginProbe ʕ •ᴥ•ʔ
Premium Addons for Elementor – Powerful Elementor Templates & Widgets / 4.11.84
Premium Addons for Elementor – Powerful Elementor Templates & Widgets v4.11.84
4.11.84 4.11.83 4.11.82 4.11.80 4.11.81 4.11.79 4.11.78 4.11.77 4.11.76 4.11.75 3.20.5 4.11.69 3.20.6 4.11.7 3.20.7 4.11.70 3.20.8 4.11.71 3.20.9 4.11.72 3.21.1 4.11.73 3.21.2 4.11.74 3.21.3 4.11.8 3.21.4 4.11.9 3.21.5 4.2.0 3.21.6 4.2.1 3.3.0 4.2.2 3.3.1 4.2.3 3.3.2 4.2.4 3.3.3 4.2.5 3.3.4 4.2.6 3.3.5 4.2.7 3.3.6 4.2.8 3.3.7 4.2.9 3.3.8 4.3.0 3.3.9 4.3.1 3.4.0 4.3.2 3.4.1 4.3.3 3.4.2 4.3.4 3.4.3 4.3.5 3.4.4 4.3.6 3.4.5 4.3.7 3.4.6 4.3.8 3.4.7 4.3.9 3.4.8 4.4.0 3.4.9 4.4.1 3.5.0 4.4.2 3.5.1 4.4.3 3.5.2 4.4.4 3.5.3 4.4.5 3.5.4 4.4.6 3.5.5 4.4.7 3.5.6 4.4.8 3.5.7 4.4.9 3.5.8 4.5.0 3.5.9 4.5.1 3.6.0 4.5.2 3.6.1 4.5.3 3.6.2 4.5.4 3.6.3 4.5.5 3.6.4 4.5.6 3.6.5 4.5.7 3.6.6 4.5.8 3.6.7 4.5.9 3.6.8 4.6.0 3.6.9 4.6.1 3.7.0 4.7.0 3.7.1 4.7.1 3.7.2 4.7.2 3.7.3 4.7.3 3.7.4 4.7.4 3.7.5 4.7.5 3.7.6 4.7.6 3.7.7 4.7.7 3.7.8 4.7.8 3.7.9 4.7.9 3.8.0 4.8.0 3.8.1 4.8.1 3.8.2 4.8.10 3.8.3 4.8.11 3.8.4 4.8.2 3.8.5 4.8.3 3.8.6 4.8.4 3.8.7 4.8.5 3.8.8 4.8.6 3.8.9 4.8.7 3.9.0 4.8.8 3.9.1 4.8.9 3.9.2 4.9.0 3.9.3 4.9.0-beta1 3.9.4 4.9.0-beta2 3.9.5 4.9.1 3.9.6 4.9.10 3.9.7 4.9.11 3.9.8 4.9.12 3.9.9 4.9.13 4.0.1 4.9.14 4.0.3 4.9.15 4.0.4 4.9.16 4.0.5 4.9.17 4.0.6 4.9.18 4.0.7 4.9.19 4.0.8 4.9.2 4.0.9 4.9.20 4.1.0 4.9.21 4.1.1 4.9.22 4.1.2 4.9.23 4.1.3 4.9.24 trunk 4.1.4 4.9.25 1.0 4.1.5 4.9.26 1.01 4.1.6 4.9.27 1.02 4.1.7 4.9.28 1.03 4.1.8 4.9.29 1.04 4.1.9 4.9.3 1.05 4.10.0 4.9.30 1.06 4.10.1 4.9.31 1.07 4.10.10 4.9.32 1.08 4.10.11 4.9.33 1.09 4.10.12 4.9.34 2.0 4.10.13 4.9.35 2.0.1 4.10.14 4.9.36 2.0.2 4.10.15 4.9.37 2.0.3 4.10.16 4.9.38 2.0.4 4.10.17 4.9.39 2.0.5 4.10.18 4.9.4 2.0.6 4.10.19 4.9.40 2.0.7 4.10.2 4.9.41 2.0.8 4.10.20 4.9.42 2.0.9 4.10.21 4.9.43 2.1.0 4.10.22 4.9.45 2.1.1 4.10.23 4.9.46 2.1.2 4.10.24 4.9.47 2.1.3 4.10.25 4.9.48 2.1.4 4.10.26 4.9.49 2.1.5 4.10.27 4.9.5 2.1.5-beta1 4.10.28 4.9.50 2.1.6 4.10.29 4.9.51 2.1.7 4.10.3 4.9.52 2.1.8 4.10.30 4.9.53 2.1.9 4.10.31 4.9.54 2.2.0 4.10.32 4.9.55 2.2.1 4.10.33 4.9.56 2.2.2 4.10.34 4.9.57 2.2.3 4.10.35 4.9.6 2.2.4 4.10.36 4.9.7 2.2.5 4.10.37 4.9.8 2.2.6 4.10.38 4.9.9 2.2.7 4.10.39 2.2.8 4.10.4 2.2.9 4.10.40 2.3.0 4.10.41 2.3.1 4.10.42 2.3.2 4.10.43 2.3.3 4.10.44 2.3.4 4.10.45 2.3.5 4.10.46 2.3.6 4.10.47 2.3.7 4.10.48 2.3.8 4.10.49 2.3.9 4.10.5 2.4.0 4.10.50 2.4.1 4.10.51 2.5.0 4.10.52 2.5.1 4.10.53 2.5.2 4.10.54 2.5.3 4.10.55 2.5.4 4.10.56 2.5.5 4.10.57 2.5.6 4.10.58 2.5.7 4.10.59 2.5.8 4.10.6 2.5.9 4.10.60 2.6.0 4.10.61 2.6.1 4.10.62 2.6.2 4.10.63 2.6.3 4.10.64 2.6.4 4.10.65 2.6.5 4.10.66 2.6.6 4.10.67 2.6.7 4.10.68 2.6.8 4.10.69 2.6.9 4.10.7 2.7.0 4.10.70 2.7.1 4.10.71 2.7.2 4.10.72 2.7.3 4.10.73 2.7.4 4.10.74 2.7.5 4.10.75 2.7.6 4.10.76 2.7.7 4.10.77 2.7.8 4.10.78 2.7.9 4.10.79 2.8.0 4.10.8 2.8.1 4.10.80 2.8.2 4.10.81 2.8.3 4.10.82 2.8.4 4.10.83 2.8.5 4.10.84 2.8.6 4.10.85 2.8.7 4.10.86 2.8.8 4.10.87 2.8.9 4.10.88 2.9.0 4.10.89 2.9.1 4.10.9 2.9.2 4.10.90 2.9.3 4.11.0 2.9.4 4.11.1 2.9.5 4.11.10 2.9.6 4.11.11 2.9.7 4.11.12 2.9.8 4.11.13 2.9.9 4.11.14 3.0.0 4.11.15 3.0.1 4.11.16 3.0.2 4.11.17 3.0.3 4.11.18 3.0.4 4.11.19 3.0.5 4.11.2 3.0.6 4.11.20 3.0.7 4.11.21 3.0.8 4.11.22 3.0.9 4.11.23 3.1.0 4.11.24 3.1.1 4.11.25 3.1.2 4.11.26 3.1.3 4.11.27 3.1.4 4.11.28 3.1.5 4.11.29 3.1.6 4.11.3 3.1.7 4.11.30 3.1.8 4.11.31 3.1.9 4.11.32 3.10.0 4.11.33 3.10.1 4.11.34 3.10.2 4.11.35 3.10.3 4.11.36 3.10.4 4.11.37 3.10.5 4.11.38 3.10.6 4.11.39 3.10.7 4.11.4 3.10.8 4.11.40 3.10.9 4.11.41 3.11.0 4.11.42 3.11.1 4.11.43 3.11.2 4.11.44 3.11.3 4.11.45 3.11.4 4.11.46 3.11.5 4.11.47 3.11.6 4.11.48 3.11.7 4.11.49 3.11.8 4.11.5 3.11.9 4.11.50 3.12.0 4.11.51 3.12.1 4.11.52 3.12.2 4.11.53 3.12.3 4.11.54 3.2.0 4.11.55 3.2.1 4.11.56 3.2.2 4.11.57 3.2.3 4.11.58 3.2.4 4.11.59 3.2.5 4.11.6 3.2.6 4.11.60 3.2.7 4.11.61 3.2.8 4.11.62 3.2.9 4.11.63 3.20.0 4.11.64 3.20.1 4.11.65 3.20.2 4.11.66 3.20.3 4.11.67 3.20.4 4.11.68
premium-addons-for-elementor / includes / helpers / device-detector.php
premium-addons-for-elementor / includes / helpers Last commit date
acf-helper.php 2 months ago ajax-helper.php 2 months ago device-detector.php 2 months ago pa-nav-menu-walker.php 2 months ago papro-promotion.php 1 day ago query-helper.php 2 days ago widget-class-map.php 2 months ago widget-name-map.php 2 months ago
device-detector.php
1470 lines
1 <?php
2
3 /**
4 * Mobile Detect Library
5 * Motto: "Every business should have a mobile detection script to detect mobile readers"
6 *
7 * Device_Detector is a lightweight PHP class for detecting mobile devices (including tablets).
8 * It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
9 *
10 * Homepage: http://mobiledetect.net
11 * GitHub: https://github.com/serbanghita/Mobile-Detect
12 * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md
13 * CONTRIBUTING: https://github.com/serbanghita/Mobile-Detect/blob/master/docs/CONTRIBUTING.md
14 * KNOWN LIMITATIONS: https://github.com/serbanghita/Mobile-Detect/blob/master/docs/KNOWN_LIMITATIONS.md
15 * EXAMPLES: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
16 *
17 * @license https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt MIT License
18 * @author Serban Ghita <serbanghita@gmail.com>
19 * @author Nick Ilyin <nick.ilyin@gmail.com>
20 * Original author: Victor Stanciu <vic.stanciu@gmail.com>
21 *
22 * @version 2.8.36
23 */
24
25 namespace PremiumAddons\Includes\Helpers;
26
27 class Device_Detector {
28
29 /**
30 * Mobile detection type.
31 *
32 * @deprecated since version 2.6.9
33 */
34 const DETECTION_TYPE_MOBILE = 'mobile';
35
36 /**
37 * Extended detection type.
38 *
39 * @deprecated since version 2.6.9
40 */
41 const DETECTION_TYPE_EXTENDED = 'extended';
42
43 /**
44 * A frequently used regular expression to extract version #s.
45 *
46 * @deprecated since version 2.6.9
47 */
48 const VER = '([\w._\+]+)';
49
50 /**
51 * Top-level device.
52 */
53 const MOBILE_GRADE_A = 'A';
54
55 /**
56 * Mid-level device.
57 */
58 const MOBILE_GRADE_B = 'B';
59
60 /**
61 * Low-level device.
62 */
63 const MOBILE_GRADE_C = 'C';
64
65 /**
66 * Stores the version number of the current release.
67 */
68 const VERSION = '2.8.36';
69
70 /**
71 * A type for the version() method indicating a string return value.
72 */
73 const VERSION_TYPE_STRING = 'text';
74
75 /**
76 * A type for the version() method indicating a float return value.
77 */
78 const VERSION_TYPE_FLOAT = 'float';
79
80 /**
81 * A cache for resolved matches
82 *
83 * @var array
84 */
85 protected $cache = array();
86
87 /**
88 * The User-Agent HTTP header is stored in here.
89 *
90 * @var string
91 */
92 protected $userAgent = null;
93
94 /**
95 * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
96 *
97 * @var array
98 */
99 protected $httpHeaders = array();
100
101 /**
102 * CloudFront headers. E.g. CloudFront-Is-Desktop-Viewer, CloudFront-Is-Mobile-Viewer & CloudFront-Is-Tablet-Viewer.
103 *
104 * @var array
105 */
106 protected $cloudfrontHeaders = array();
107
108 /**
109 * The matching Regex.
110 * This is good for debug.
111 *
112 * @var string
113 */
114 protected $matchingRegex = null;
115
116 /**
117 * The matches extracted from the regex expression.
118 * This is good for debug.
119 *
120 * @var string
121 */
122 protected $matchesArray = null;
123
124 /**
125 * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED.
126 *
127 * @deprecated since version 2.6.9
128 *
129 * @var string
130 */
131 protected $detectionType = self::DETECTION_TYPE_MOBILE;
132
133 /**
134 * HTTP headers that trigger the 'isMobile' detection
135 * to be true.
136 *
137 * @var array
138 */
139 protected static $mobileHeaders = array(
140
141 'HTTP_ACCEPT' => array(
142 'matches' => array(
143 // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
144 'application/x-obml2d',
145 // BlackBerry devices.
146 'application/vnd.rim.html',
147 'text/vnd.wap.wml',
148 'application/vnd.wap.xhtml+xml',
149 ),
150 ),
151 'HTTP_X_WAP_PROFILE' => null,
152 'HTTP_X_WAP_CLIENTID' => null,
153 'HTTP_WAP_CONNECTION' => null,
154 'HTTP_PROFILE' => null,
155 // Reported by Opera on Nokia devices (eg. C3).
156 'HTTP_X_OPERAMINI_PHONE_UA' => null,
157 'HTTP_X_NOKIA_GATEWAY_ID' => null,
158 'HTTP_X_ORANGE_ID' => null,
159 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
160 'HTTP_X_HUAWEI_USERID' => null,
161 // Reported by Windows Smartphones.
162 'HTTP_UA_OS' => null,
163 // Reported by Verizon, Vodafone proxy system.
164 'HTTP_X_MOBILE_GATEWAY' => null,
165 // Seen this on HTC Sensation. SensationXE_Beats_Z715e.
166 'HTTP_X_ATT_DEVICEID' => null,
167 // Seen this on a HTC.
168 'HTTP_UA_CPU' => array( 'matches' => array( 'ARM' ) ),
169 );
170
171 /**
172 * List of mobile devices (phones).
173 *
174 * @var array
175 */
176 protected static $phoneDevices = array(
177 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
178 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+|\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\b-[0-9]+',
179 'Pixel' => '; \bPixel\b',
180 'HTC' => 'HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\bEVO\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel',
181 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6',
182 // @todo: Is 'Dell Streak' a tablet or a phone? ;)
183 'Dell' => 'Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
184 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b|XT1068|XT1092|XT1052',
185 'Samsung' => '\bSamsung\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F|SM-G610F|SM-G981B',
186 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710',
187 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533',
188 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile',
189 'NokiaLumia' => 'Lumia [0-9]{3,4}',
190 // http://www.micromaxinfo.com/mobiles/smartphones
191 // Added because the codes might conflict with Acer Tablets.
192 'Micromax' => 'Micromax.*\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\b',
193 // @todo Complete the regex.
194 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
195 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
196 // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
197 // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
198 'Pantech' => 'PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790',
199 // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
200 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
201 // http://fr.wikomobile.com
202 'Wiko' => 'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM',
203 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
204 // Added simvalley mobile just for fun. They have some interesting devices.
205 // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
206 'SimValley' => '\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\b',
207 // Wolfgang - a brand that is sold by Aldi supermarkets.
208 // http://www.wolfgangmobile.com/
209 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
210 'Alcatel' => 'Alcatel',
211 'Nintendo' => 'Nintendo (3DS|Switch)',
212 // http://en.wikipedia.org/wiki/Amoi
213 'Amoi' => 'Amoi',
214 // http://en.wikipedia.org/wiki/INQ
215 'INQ' => 'INQ',
216 'OnePlus' => 'ONEPLUS',
217 // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
218 'GenericPhone' => 'Tapatalk|PDA;|SAGEM|\bmmp\b|pocket|\bpsp\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\bwap\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser',
219 );
220
221 /**
222 * List of tablet devices.
223 *
224 * @var array
225 */
226 protected static $tabletDevices = array(
227 // @todo: check for mobile friendly emails topic.
228 'iPad' => 'iPad|iPad.*Mobile',
229 // Removed |^.*Android.*Nexus(?!(?:Mobile).)*$
230 // @see #442
231 // @todo Merge NexusTablet into GoogleTablet.
232 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
233 // https://en.wikipedia.org/wiki/Pixel_C
234 'GoogleTablet' => 'Android.*Pixel C',
235 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
236 // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
237 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
238 // Only the Surface tablets with Windows RT are considered mobile.
239 // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
240 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
241 // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
242 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
243 // Watch out for PadFone, see #132.
244 // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
245 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
246 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
247 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
248 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
249 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
250 // http://www.acer.ro/ac/ro/RO/content/drivers
251 // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
252 // http://us.acer.com/ac/en/US/content/group/tablets
253 // http://www.acer.de/ac/de/DE/content/models/tablets/
254 // Can conflict with Micromax and Motorola phones codes.
255 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30',
256 // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
257 // http://us.toshiba.com/tablets/tablet-finder
258 // http://www.toshiba.co.jp/regza/tablet/
259 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
260 // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
261 // http://www.lg.com/us/tablets
262 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
263 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
264 // Prestigio Tablets http://www.prestigio.com/support
265 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
266 // http://support.lenovo.com/en_GB/downloads/default.page?#
267 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X',
268 // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
269 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
270 // http://www.yarvik.com/en/matrix/tablets/
271 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
272 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
273 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
274 // http://www.intenso.de/kategorie_en.php?kategorie=33
275 // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
276 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
277 // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
278 'IRUTablet' => 'M702pro',
279 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
280 // http://www.e-boda.ro/tablete-pc.html
281 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
282 // http://www.allview.ro/produse/droseries/lista-tablete-pc/
283 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
284 // http://wiki.archosfans.com/index.php?title=Main_Page
285 // @note Rewrite the regex format after we add more UAs.
286 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
287 // http://www.ainol.com/plugin.php?identifier=ainol&module=product
288 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
289 'NokiaLumiaTablet' => 'Lumia 2520',
290 // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
291 // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
292 // http://www.sony.jp/support/tablet/
293 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
294 // http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8
295 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
296 // db + http://www.cube-tablet.com/buy-products.html
297 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
298 // http://www.cobyusa.com/?p=pcat&pcat_id=3001
299 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
300 // http://www.match.net.cn/products.asp
301 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
302 // http://www.msi.com/support
303 // @todo Research the Windows Tablets.
304 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
305 // @todo http://www.kyoceramobile.com/support/drivers/
306 // 'KyoceraTablet' => null,
307 // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
308 // 'IntextTablet' => null,
309 // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
310 // http://www.imp3.net/14/show.php?itemid=20454
311 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
312 // http://www.rock-chips.com/index.php?do=prod&pid=2
313 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
314 // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
315 'FlyTablet' => 'IQ310|Fly Vision',
316 // http://www.bqreaders.com/gb/tablets-prices-sale.html
317 'bqTablet' => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
318 // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
319 // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
320 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19',
321 // Nec or Medias Tab
322 'NecTablet' => '\bN-06D|\bN-08D',
323 // Pantech Tablets: http://www.pantechusa.com/phones/
324 'PantechTablet' => 'Pantech.*P4100',
325 // Broncho Tablets: http://www.broncho.cn/ (hard to find)
326 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
327 // http://versusuk.com/support.html
328 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
329 // http://www.zync.in/index.php/our-products/tablet-phablets
330 'ZyncTablet' => 'z1000|Z99 2G|z930|z990|z909|Z919|z900', // Removed "z999" because of https://github.com/serbanghita/Mobile-Detect/issues/717
331 // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
332 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
333 // https://www.nabitablet.com/
334 'NabiTablet' => 'Android.*\bNabi',
335 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
336 // French Danew Tablets http://www.danew.com/produits-tablette.php
337 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
338 // Texet Tablets and Readers http://www.texet.ru/tablet/
339 'TexetTablet' => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
340 // Avoid detecting 'PLAYSTATION 3' as mobile.
341 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
342 // http://www.trekstor.de/surftabs.html
343 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
344 // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
345 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
346 // http://www.advandigital.com/index.php?link=content-product&jns=JP001
347 // because of the short codenames we have to include whitespaces to reduce the possible conflicts.
348 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
349 // http://www.danytech.com/category/tablet-pc
350 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
351 // http://www.galapad.net/product.html ; https://github.com/serbanghita/Mobile-Detect/issues/761
352 'GalapadTablet' => 'Android [0-9.]+; [a-z-]+; \bG1\b',
353 // http://www.micromaxinfo.com/tablet/funbook
354 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
355 // http://www.karbonnmobiles.com/products_tablet.php
356 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
357 // http://www.myallfine.com/Products.asp
358 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
359 // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
360 'PROSCANTablet' => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
361 // http://www.yonesnav.com/products/products.php
362 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
363 // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
364 // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
365 'ChangJiaTablet' => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
366 // http://www.gloryunion.cn/products.asp
367 // http://www.allwinnertech.com/en/apply/mobile.html
368 // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
369 // @todo: Softwiner tablets?
370 // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
371 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
372 // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
373 'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
374 // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
375 // @todo: add more tests.
376 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
377 // http://hclmetablet.com/India/index.php
378 'HCLTablet' => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
379 // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
380 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
381 // http://www.visture.com/index.asp
382 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
383 // http://www.mijncresta.nl/tablet
384 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
385 // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
386 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
387 // Concorde tab
388 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
389 // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
390 'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
391 // Modecom Tablets - http://www.modecom.eu/tablets/portal/
392 'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
393 // Vonino Tablets
394 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
395 // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
396 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
397 // Storex Tablets - http://storex.fr/espace_client/support.html
398 // @note: no need to add all the tablet codes since they are guided by the first regex.
399 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
400 // Generic Vodafone tablets.
401 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
402 // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
403 // Aka: http://www.essentielb.fr/
404 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
405 // Ross & Moor - http://ross-moor.ru/
406 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
407 // i-mobile http://product.i-mobilephone.com/Mobile_Device
408 'iMobileTablet' => 'i-mobile i-note',
409 // http://www.tolino.de/de/vergleichen/
410 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
411 // AudioSonic - a Kmart brand
412 // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72&currentPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1
413 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
414 // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
415 // @todo: add them gradually to avoid conflicts.
416 'AMPETablet' => 'Android.* A78 ',
417 // Skk Mobile - http://skkmobile.com.ph/product_tablets.php
418 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
419 // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
420 'TecnoTablet' => 'TECNO P9|TECNO DP8D',
421 // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
422 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
423 // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
424 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
425 // http://www.intracon.eu/tablet
426 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
427 // http://www.xoro.de/produkte/
428 // @note: Might be the same brand with 'Simply tablets'
429 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
430 // http://www1.viewsonic.com/products/computing/tablets/
431 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
432 // https://www.verizonwireless.com/tablets/verizon/
433 'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
434 // http://www.odys.de/web/internet-tablet_en.html
435 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
436 // http://www.captiva-power.de/products.html#tablets-en
437 'CaptivaTablet' => 'CAPTIVA PAD',
438 // IconBIT - http://www.iconbit.com/products/tablets/
439 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
440 // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
441 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
442 // Onda - http://www.onda-tablet.com/buy-android-onda.html?dir=desc&limit=all&order=price
443 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
444 'JaytechTablet' => 'TPC-PA762',
445 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
446 // http://www.digma.ru/support/download/
447 // @todo: Ebooks also (if requested)
448 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
449 // http://www.evolioshop.com/ro/tablete-pc.html
450 // http://www.evolio.ro/support/downloads_static.html?cat=2
451 // @todo: Research some more
452 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
453 // @todo http://www.lavamobiles.com/tablets-data-cards
454 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
455 // http://www.breezetablet.com/
456 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
457 // http://www.mpmaneurope.com/en/products/internet-tablets-14/android-tablets-14/
458 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
459 // https://www.celkonmobiles.com/?_a=categoryphones&sid=2
460 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
461 // http://www.wolderelectronics.com/productos/manuales-y-guias-rapidas/categoria-2-miTab
462 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
463 'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
464 // http://www.mi.com/en
465 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
466 // http://www.nbru.cn/index.html
467 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
468 // http://navroad.com/products/produkty/tablety/
469 // http://navroad.com/products/produkty/tablety/
470 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
471 // http://leader-online.com/new_site/product-category/tablets/
472 // http://www.leader-online.net.au/List/Tablet
473 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
474 // http://www.datawind.com/ubislate/
475 'UbislateTablet' => 'UbiSlate[\s]?7C',
476 // http://www.pocketbook-int.com/ru/support
477 'PocketBookTablet' => 'Pocketbook',
478 // http://www.kocaso.com/product_tablet.html
479 'KocasoTablet' => '\b(TB-1207)\b',
480 // http://global.hisense.com/product/asia/tablet/Sero7/201412/t20141215_91832.htm
481 'HisenseTablet' => '\b(F5281|E2371)\b',
482 // http://www.tesco.com/direct/hudl/
483 'Hudl' => 'Hudl HT7S3|Hudl 2',
484 // http://www.telstra.com.au/home-phone/thub-2/
485 'TelstraTablet' => 'T-Hub2',
486 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107',
487 );
488
489 /**
490 * List of mobile Operating Systems.
491 *
492 * @var array
493 */
494 protected static $operatingSystems = array(
495 'AndroidOS' => 'Android',
496 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
497 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
498 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
499 // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
500 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;',
501 // @reference: http://en.wikipedia.org/wiki/Windows_Phone
502 // http://wifeng.cn/?r=blog&a=view&id=106
503 // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
504 // http://msdn.microsoft.com/library/ms537503.aspx
505 // https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
506 'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
507 'iOS' => '\biPhone.*Mobile|\biPod|\biPad|AppleCoreMedia',
508 // https://en.wikipedia.org/wiki/IPadOS
509 'iPadOS' => 'CPU OS 13',
510 // @reference https://en.m.wikipedia.org/wiki/Sailfish_OS
511 // https://sailfishos.org/
512 'SailfishOS' => 'Sailfish',
513 // http://en.wikipedia.org/wiki/MeeGo
514 // @todo: research MeeGo in UAs
515 'MeeGoOS' => 'MeeGo',
516 // http://en.wikipedia.org/wiki/Maemo
517 // @todo: research Maemo in UAs
518 'MaemoOS' => 'Maemo',
519 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
520 'webOS' => 'webOS|hpwOS',
521 'badaOS' => '\bBada\b',
522 'BREWOS' => 'BREW',
523 );
524
525 /**
526 * List of mobile User Agents.
527 *
528 * IMPORTANT: This is a list of only mobile browsers.
529 * Mobile Detect 2.x supports only mobile browsers,
530 * it was never designed to detect all browsers.
531 * The change will come in 2017 in the 3.x release for PHP7.
532 *
533 * @var array
534 */
535 protected static $browsers = array(
536 // 'Vivaldi' => 'Vivaldi',
537 // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
538 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?',
539 'Dolfin' => '\bDolfin\b',
540 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+$|Coast/[0-9.]+',
541 'Skyfire' => 'Skyfire',
542 // Added "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
543 'Edge' => '\bEdgiOS\b|Mobile Safari/[.0-9]* Edge',
544 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
545 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS',
546 'Bolt' => 'bolt',
547 'TeaShark' => 'teashark',
548 'Blazer' => 'Blazer',
549 // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
550 // Excluded "Edge on iOS" https://github.com/serbanghita/Mobile-Detect/issues/764
551 'Safari' => 'Version((?!\bEdgiOS\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari',
552 // http://en.wikipedia.org/wiki/Midori_(web_browser)
553 // 'Midori' => 'midori',
554 // 'Tizen' => 'Tizen',
555 'WeChat' => '\bMicroMessenger\b',
556 'UCBrowser' => 'UC.*Browser|UCWEB',
557 'baiduboxapp' => 'baiduboxapp',
558 'baidubrowser' => 'baidubrowser',
559 // https://github.com/serbanghita/Mobile-Detect/issues/7
560 'DiigoBrowser' => 'DiigoBrowser',
561 // http://www.puffinbrowser.com/index.php
562 // https://github.com/serbanghita/Mobile-Detect/issues/752
563 // 'Puffin' => 'Puffin',
564 // http://mercury-browser.com/index.html
565 'Mercury' => '\bMercury\b',
566 // http://en.wikipedia.org/wiki/Obigo_Browser
567 'ObigoBrowser' => 'Obigo',
568 // http://en.wikipedia.org/wiki/NetFront
569 'NetFront' => 'NF-Browser',
570 // @reference: http://en.wikipedia.org/wiki/Minimo
571 // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
572 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
573 // @reference: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)
574 'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon',
575 );
576
577 /**
578 * Utilities.
579 *
580 * @var array
581 */
582 protected static $utilities = array(
583 // Experimental. When a mobile device wants to switch to 'Desktop Mode'.
584 // http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
585 // https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
586 // https://developers.facebook.com/docs/sharing/webmasters/crawler/
587 'Bot' => 'Googlebot|facebookexternalhit|Google-AMPHTML|s~amp-validator|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom|contentkingapp|AspiegelBot',
588 'MobileBot' => 'Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker/M1A1-R2D2',
589 'DesktopMode' => 'WPDesktop',
590 'TV' => 'SonyDTV|HbbTV', // experimental
591 'WebKit' => '(webkit)[ /]([\w.]+)',
592 // @todo: Include JXD consoles.
593 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|Nintendo Switch|PLAYSTATION|Xbox)\b',
594 'Watch' => 'SM-V700',
595 );
596
597 /**
598 * All possible HTTP headers that represent the
599 * User-Agent string.
600 *
601 * @var array
602 */
603 protected static $uaHttpHeaders = array(
604 // The default User-Agent string.
605 'HTTP_USER_AGENT',
606 // Header can occur on devices using Opera Mini.
607 'HTTP_X_OPERAMINI_PHONE_UA',
608 // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
609 'HTTP_X_DEVICE_USER_AGENT',
610 'HTTP_X_ORIGINAL_USER_AGENT',
611 'HTTP_X_SKYFIRE_PHONE',
612 'HTTP_X_BOLT_PHONE_UA',
613 'HTTP_DEVICE_STOCK_UA',
614 'HTTP_X_UCBROWSER_DEVICE_UA',
615 );
616
617 /**
618 * The individual segments that could exist in a User-Agent string. VER refers to the regular
619 * expression defined in the constant self::VER.
620 *
621 * @var array
622 */
623 protected static $properties = array(
624
625 // Build
626 'Mobile' => 'Mobile/[VER]',
627 'Build' => 'Build/[VER]',
628 'Version' => 'Version/[VER]',
629 'VendorID' => 'VendorID/[VER]',
630
631 // Devices
632 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
633 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
634 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
635 // 'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
636 'Kindle' => 'Kindle/[VER]',
637
638 // Browser
639 'Chrome' => array( 'Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]' ),
640 'Coast' => array( 'Coast/[VER]' ),
641 'Dolfin' => 'Dolfin/[VER]',
642 // @reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
643 'Firefox' => array( 'Firefox/[VER]', 'FxiOS/[VER]' ),
644 'Fennec' => 'Fennec/[VER]',
645 // http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
646 // https://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx
647 'Edge' => 'Edge/[VER]',
648 'IE' => array( 'IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]' ),
649 // http://en.wikipedia.org/wiki/NetFront
650 'NetFront' => 'NetFront/[VER]',
651 'NokiaBrowser' => 'NokiaBrowser/[VER]',
652 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
653 'Opera Mini' => 'Opera Mini/[VER]',
654 'Opera Mobi' => 'Version/[VER]',
655 'UCBrowser' => array( 'UCWEB[VER]', 'UC.*Browser/[VER]' ),
656 'MQQBrowser' => 'MQQBrowser/[VER]',
657 'MicroMessenger' => 'MicroMessenger/[VER]',
658 'baiduboxapp' => 'baiduboxapp/[VER]',
659 'baidubrowser' => 'baidubrowser/[VER]',
660 'SamsungBrowser' => 'SamsungBrowser/[VER]',
661 'Iron' => 'Iron/[VER]',
662 // @note: Safari 7534.48.3 is actually Version 5.1.
663 // @note: On BlackBerry the Version is overwriten by the OS.
664 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
665 'Skyfire' => 'Skyfire/[VER]',
666 'Tizen' => 'Tizen/[VER]',
667 'Webkit' => 'webkit[ /][VER]',
668 'PaleMoon' => 'PaleMoon/[VER]',
669 'SailfishBrowser' => 'SailfishBrowser/[VER]',
670
671 // Engine
672 'Gecko' => 'Gecko/[VER]',
673 'Trident' => 'Trident/[VER]',
674 'Presto' => 'Presto/[VER]',
675 'Goanna' => 'Goanna/[VER]',
676
677 // OS
678 'iOS' => ' \bi?OS\b [VER][ ;]{1}',
679 'Android' => 'Android [VER]',
680 'Sailfish' => 'Sailfish [VER]',
681 'BlackBerry' => array( 'BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]' ),
682 'BREW' => 'BREW [VER]',
683 'Java' => 'Java/[VER]',
684 // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
685 // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
686 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]' ),
687 'Windows Phone' => 'Windows Phone [VER]',
688 'Windows CE' => 'Windows CE/[VER]',
689 // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
690 'Windows NT' => 'Windows NT [VER]',
691 'Symbian' => array( 'SymbianOS/[VER]', 'Symbian/[VER]' ),
692 'webOS' => array( 'webOS/[VER]', 'hpwOS/[VER];' ),
693 );
694
695 /**
696 * Construct an instance of this class.
697 *
698 * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored.
699 * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
700 * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
701 * from the $headers array instead.
702 */
703 public function __construct(
704 ?array $headers = null,
705 $userAgent = null
706 ) {
707 $this->setHttpHeaders( $headers );
708 $this->setUserAgent( $userAgent );
709 }
710
711 /**
712 * Get the current script version.
713 * This is useful for the demo.php file,
714 * so people can check on what version they are testing
715 * for mobile devices.
716 *
717 * @return string The version number in semantic version format.
718 */
719 public static function getScriptVersion() {
720 return self::VERSION;
721 }
722
723 /**
724 * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
725 *
726 * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
727 * the headers. The default null is left for backwards compatibility.
728 */
729 public function setHttpHeaders( $httpHeaders = null ) {
730 // use global _SERVER if $httpHeaders aren't defined
731 if ( ! is_array( $httpHeaders ) || ! count( $httpHeaders ) ) {
732 $httpHeaders = $_SERVER;
733 }
734
735 // clear existing headers
736 $this->httpHeaders = array();
737
738 // Only save HTTP headers. In PHP land, that means only _SERVER vars that
739 // start with HTTP_.
740 foreach ( $httpHeaders as $key => $value ) {
741 if ( substr( $key, 0, 5 ) === 'HTTP_' ) {
742 $this->httpHeaders[ $key ] = $value;
743 }
744 }
745
746 // In case we're dealing with CloudFront, we need to know.
747 $this->setCfHeaders( $httpHeaders );
748 }
749
750 /**
751 * Retrieves the HTTP headers.
752 *
753 * @return array
754 */
755 public function getHttpHeaders() {
756 return $this->httpHeaders;
757 }
758
759 /**
760 * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
761 * Simply null is returned.
762 *
763 * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
764 * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
765 * all-caps, HTTP_ prefixed, underscore seperated awesomeness.
766 *
767 * @return string|null The value of the header.
768 */
769 public function getHttpHeader( $header ) {
770 // are we using PHP-flavored headers?
771 if ( strpos( $header, '_' ) === false ) {
772 $header = str_replace( '-', '_', $header );
773 $header = strtoupper( $header );
774 }
775
776 // test the alternate, too
777 $altHeader = 'HTTP_' . $header;
778
779 // Test both the regular and the HTTP_ prefix
780 if ( isset( $this->httpHeaders[ $header ] ) ) {
781 return $this->httpHeaders[ $header ];
782 } elseif ( isset( $this->httpHeaders[ $altHeader ] ) ) {
783 return $this->httpHeaders[ $altHeader ];
784 }
785
786 return null;
787 }
788
789 public function getMobileHeaders() {
790 return self::$mobileHeaders;
791 }
792
793 /**
794 * Get all possible HTTP headers that
795 * can contain the User-Agent string.
796 *
797 * @return array List of HTTP headers.
798 */
799 public function getUaHttpHeaders() {
800 return self::$uaHttpHeaders;
801 }
802
803
804 /**
805 * Set CloudFront headers
806 * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device
807 *
808 * @param array $cfHeaders List of HTTP headers
809 *
810 * @return boolean If there were CloudFront headers to be set
811 */
812 public function setCfHeaders( $cfHeaders = null ) {
813 // use global _SERVER if $cfHeaders aren't defined
814 if ( ! is_array( $cfHeaders ) || ! count( $cfHeaders ) ) {
815 $cfHeaders = $_SERVER;
816 }
817
818 // clear existing headers
819 $this->cloudfrontHeaders = array();
820
821 // Only save CLOUDFRONT headers. In PHP land, that means only _SERVER vars that
822 // start with cloudfront-.
823 $response = false;
824 foreach ( $cfHeaders as $key => $value ) {
825 if ( substr( strtolower( $key ), 0, 16 ) === 'http_cloudfront_' ) {
826 $this->cloudfrontHeaders[ strtoupper( $key ) ] = $value;
827 $response = true;
828 }
829 }
830
831 return $response;
832 }
833
834 /**
835 * Retrieves the cloudfront headers.
836 *
837 * @return array
838 */
839 public function getCfHeaders() {
840 return $this->cloudfrontHeaders;
841 }
842
843 /**
844 * @param string $userAgent
845 * @return string
846 */
847 private function prepareUserAgent( $userAgent ) {
848 $userAgent = trim( $userAgent );
849 $userAgent = substr( $userAgent, 0, 500 );
850 return $userAgent;
851 }
852
853 /**
854 * Set the User-Agent to be used.
855 *
856 * @param string $userAgent The user agent string to set.
857 *
858 * @return string|null
859 */
860 public function setUserAgent( $userAgent = null ) {
861 // Invalidate cache due to #375
862 $this->cache = array();
863
864 if ( false === empty( $userAgent ) ) {
865 return $this->userAgent = $this->prepareUserAgent( $userAgent );
866 } else {
867 $this->userAgent = null;
868 foreach ( $this->getUaHttpHeaders() as $altHeader ) {
869 if ( false === empty( $this->httpHeaders[ $altHeader ] ) ) { // @todo: should use getHttpHeader(), but it would be slow. (Serban)
870 $this->userAgent .= $this->httpHeaders[ $altHeader ] . ' ';
871 }
872 }
873
874 if ( ! empty( $this->userAgent ) ) {
875 return $this->userAgent = $this->prepareUserAgent( $this->userAgent );
876 }
877 }
878
879 if ( count( $this->getCfHeaders() ) > 0 ) {
880 return $this->userAgent = 'Amazon CloudFront';
881 }
882 return $this->userAgent = null;
883 }
884
885 /**
886 * Retrieve the User-Agent.
887 *
888 * @return string|null The user agent if it's set.
889 */
890 public function getUserAgent() {
891 return $this->userAgent;
892 }
893
894 /**
895 * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or
896 * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set.
897 *
898 * @deprecated since version 2.6.9
899 *
900 * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default
901 * parameter is null which will default to self::DETECTION_TYPE_MOBILE.
902 */
903 public function setDetectionType( $type = null ) {
904 if ( $type === null ) {
905 $type = self::DETECTION_TYPE_MOBILE;
906 }
907
908 if ( $type !== self::DETECTION_TYPE_MOBILE && $type !== self::DETECTION_TYPE_EXTENDED ) {
909 return;
910 }
911
912 $this->detectionType = $type;
913 }
914
915 public function getMatchingRegex() {
916 return $this->matchingRegex;
917 }
918
919 public function getMatchesArray() {
920 return $this->matchesArray;
921 }
922
923 /**
924 * Retrieve the list of known phone devices.
925 *
926 * @return array List of phone devices.
927 */
928 public static function getPhoneDevices() {
929 return self::$phoneDevices;
930 }
931
932 /**
933 * Retrieve the list of known tablet devices.
934 *
935 * @return array List of tablet devices.
936 */
937 public static function getTabletDevices() {
938 return self::$tabletDevices;
939 }
940
941 /**
942 * Alias for getBrowsers() method.
943 *
944 * @return array List of user agents.
945 */
946 public static function getUserAgents() {
947 return self::getBrowsers();
948 }
949
950 /**
951 * Retrieve the list of known browsers. Specifically, the user agents.
952 *
953 * @return array List of browsers / user agents.
954 */
955 public static function getBrowsers() {
956 return self::$browsers;
957 }
958
959 /**
960 * Retrieve the list of known utilities.
961 *
962 * @return array List of utilities.
963 */
964 public static function getUtilities() {
965 return self::$utilities;
966 }
967
968 /**
969 * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
970 *
971 * @deprecated since version 2.6.9
972 *
973 * @return array All the rules (but not extended).
974 */
975 public static function getMobileDetectionRules() {
976 static $rules;
977
978 if ( ! $rules ) {
979 $rules = array_merge(
980 self::$phoneDevices,
981 self::$tabletDevices,
982 self::$operatingSystems,
983 self::$browsers
984 );
985 }
986
987 return $rules;
988 }
989
990 /**
991 * Method gets the mobile detection rules + utilities.
992 * The reason this is separate is because utilities rules
993 * don't necessary imply mobile. This method is used inside
994 * the new $detect->is('stuff') method.
995 *
996 * @deprecated since version 2.6.9
997 *
998 * @return array All the rules + extended.
999 */
1000 public function getMobileDetectionRulesExtended() {
1001 static $rules;
1002
1003 if ( ! $rules ) {
1004 // Merge all rules together.
1005 $rules = array_merge(
1006 self::$phoneDevices,
1007 self::$tabletDevices,
1008 self::$operatingSystems,
1009 self::$browsers,
1010 self::$utilities
1011 );
1012 }
1013
1014 return $rules;
1015 }
1016
1017 /**
1018 * Retrieve the current set of rules.
1019 *
1020 * @deprecated since version 2.6.9
1021 *
1022 * @return array
1023 */
1024 public function getRules() {
1025 if ( $this->detectionType == self::DETECTION_TYPE_EXTENDED ) {
1026 return self::getMobileDetectionRulesExtended();
1027 } else {
1028 return self::getMobileDetectionRules();
1029 }
1030 }
1031
1032 /**
1033 * Retrieve the list of mobile operating systems.
1034 *
1035 * @return array The list of mobile operating systems.
1036 */
1037 public static function getOperatingSystems() {
1038 return self::$operatingSystems;
1039 }
1040
1041 /**
1042 * Check the HTTP headers for signs of mobile.
1043 * This is the fastest mobile check possible; it's used
1044 * inside isMobile() method.
1045 *
1046 * @return bool
1047 */
1048 public function checkHttpHeadersForMobile() {
1049
1050 foreach ( $this->getMobileHeaders() as $mobileHeader => $matchType ) {
1051 if ( isset( $this->httpHeaders[ $mobileHeader ] ) ) {
1052 if ( isset( $matchType['matches'] ) && is_array( $matchType['matches'] ) ) {
1053 foreach ( $matchType['matches'] as $_match ) {
1054 if ( strpos( $this->httpHeaders[ $mobileHeader ], $_match ) !== false ) {
1055 return true;
1056 }
1057 }
1058
1059 return false;
1060 } else {
1061 return true;
1062 }
1063 }
1064 }
1065
1066 return false;
1067 }
1068
1069 /**
1070 * Magic overloading method.
1071 *
1072 * @method boolean is[...]()
1073 * @param string $name
1074 * @param array $arguments
1075 * @return mixed
1076 * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
1077 */
1078 public function __call( $name, $arguments ) {
1079 // make sure the name starts with 'is', otherwise
1080 if ( substr( $name, 0, 2 ) !== 'is' ) {
1081 throw new BadMethodCallException( "No such method exists: $name" );
1082 }
1083
1084 $this->setDetectionType( self::DETECTION_TYPE_MOBILE );
1085
1086 $key = substr( $name, 2 );
1087
1088 return $this->matchUAAgainstKey( $key );
1089 }
1090
1091 /**
1092 * Find a detection rule that matches the current User-agent.
1093 *
1094 * @param null $userAgent deprecated
1095 * @return boolean
1096 */
1097 protected function matchDetectionRulesAgainstUA( $userAgent = null ) {
1098 // Begin general search.
1099 foreach ( $this->getRules() as $_regex ) {
1100 if ( empty( $_regex ) ) {
1101 continue;
1102 }
1103
1104 if ( $this->match( $_regex, $userAgent ) ) {
1105 return true;
1106 }
1107 }
1108
1109 return false;
1110 }
1111
1112 /**
1113 * Search for a certain key in the rules array.
1114 * If the key is found then try to match the corresponding
1115 * regex against the User-Agent.
1116 *
1117 * @param string $key
1118 *
1119 * @return boolean
1120 */
1121 protected function matchUAAgainstKey( $key ) {
1122 // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
1123 $key = strtolower( $key );
1124 if ( false === isset( $this->cache[ $key ] ) ) {
1125
1126 // change the keys to lower case
1127 $_rules = array_change_key_case( $this->getRules() );
1128
1129 if ( false === empty( $_rules[ $key ] ) ) {
1130 $this->cache[ $key ] = $this->match( $_rules[ $key ] );
1131 }
1132
1133 if ( false === isset( $this->cache[ $key ] ) ) {
1134 $this->cache[ $key ] = false;
1135 }
1136 }
1137
1138 return $this->cache[ $key ];
1139 }
1140
1141 /**
1142 * Check if the device is mobile.
1143 * Returns true if any type of mobile device detected, including special ones
1144 *
1145 * @param null $userAgent deprecated
1146 * @param null $httpHeaders deprecated
1147 * @return bool
1148 */
1149 public function isMobile( $userAgent = null, $httpHeaders = null ) {
1150
1151 if ( $httpHeaders ) {
1152 $this->setHttpHeaders( $httpHeaders );
1153 }
1154
1155 if ( $userAgent ) {
1156 $this->setUserAgent( $userAgent );
1157 }
1158
1159 // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
1160 if ( $this->getUserAgent() === 'Amazon CloudFront' ) {
1161 $cfHeaders = $this->getCfHeaders();
1162 if ( array_key_exists( 'HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders ) && $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true' ) {
1163 return true;
1164 }
1165 }
1166
1167 $this->setDetectionType( self::DETECTION_TYPE_MOBILE );
1168
1169 if ( $this->checkHttpHeadersForMobile() ) {
1170 return true;
1171 } else {
1172 return $this->matchDetectionRulesAgainstUA();
1173 }
1174 }
1175
1176 /**
1177 * Check if the device is a tablet.
1178 * Return true if any type of tablet device is detected.
1179 *
1180 * @param string $userAgent deprecated
1181 * @param array $httpHeaders deprecated
1182 * @return bool
1183 */
1184 public function isTablet( $userAgent = null, $httpHeaders = null ) {
1185 // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
1186 if ( $this->getUserAgent() === 'Amazon CloudFront' ) {
1187 $cfHeaders = $this->getCfHeaders();
1188 if ( array_key_exists( 'HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders ) && $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true' ) {
1189 return true;
1190 }
1191 }
1192
1193 $this->setDetectionType( self::DETECTION_TYPE_MOBILE );
1194
1195 if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
1196 return false;
1197 }
1198
1199 $device = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
1200
1201 foreach ( self::$tabletDevices as $_regex ) {
1202 if ( $this->match( $_regex, $device ) ) {
1203 return true;
1204 }
1205 }
1206
1207 return false;
1208 }
1209
1210 /**
1211 * This method checks for a certain property in the
1212 * userAgent.
1213 *
1214 * @todo: The httpHeaders part is not yet used.
1215 *
1216 * @param string $key
1217 * @param string $userAgent deprecated
1218 * @param string $httpHeaders deprecated
1219 * @return bool|int|null
1220 */
1221 public function is( $key, $userAgent = null, $httpHeaders = null ) {
1222 // Set the UA and HTTP headers only if needed (eg. batch mode).
1223 if ( $httpHeaders ) {
1224 $this->setHttpHeaders( $httpHeaders );
1225 }
1226
1227 if ( $userAgent ) {
1228 $this->setUserAgent( $userAgent );
1229 }
1230
1231 $this->setDetectionType( self::DETECTION_TYPE_EXTENDED );
1232
1233 return $this->matchUAAgainstKey( $key );
1234 }
1235
1236 /**
1237 * Some detection rules are relative (not standard),
1238 * because of the diversity of devices, vendors and
1239 * their conventions in representing the User-Agent or
1240 * the HTTP headers.
1241 *
1242 * This method will be used to check custom regexes against
1243 * the User-Agent string.
1244 *
1245 * @param $regex
1246 * @param string $userAgent
1247 * @return bool
1248 *
1249 * @todo: search in the HTTP headers too.
1250 */
1251 public function match( $regex, $userAgent = null ) {
1252 $match = (bool) preg_match( sprintf( '#%s#is', $regex ), ( false === empty( $userAgent ) ? $userAgent : $this->userAgent ), $matches );
1253 // If positive match is found, store the results for debug.
1254 if ( $match ) {
1255 $this->matchingRegex = $regex;
1256 $this->matchesArray = $matches;
1257 }
1258
1259 return $match;
1260 }
1261
1262 /**
1263 * Get the properties array.
1264 *
1265 * @return array
1266 */
1267 public static function getProperties() {
1268 return self::$properties;
1269 }
1270
1271 /**
1272 * Prepare the version number.
1273 *
1274 * @todo Remove the error supression from str_replace() call.
1275 *
1276 * @param string $ver The string version, like "2.6.21.2152";
1277 *
1278 * @return float
1279 */
1280 public function prepareVersionNo( $ver ) {
1281 $ver = str_replace( array( '_', ' ', '/' ), '.', $ver );
1282 $arrVer = explode( '.', $ver, 2 );
1283
1284 if ( isset( $arrVer[1] ) ) {
1285 $arrVer[1] = @str_replace( '.', '', $arrVer[1] ); // @todo: treat strings versions.
1286 }
1287
1288 return (float) implode( '.', $arrVer );
1289 }
1290
1291 /**
1292 * Check the version of the given property in the User-Agent.
1293 * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
1294 *
1295 * @param string $propertyName The name of the property. See self::getProperties() array
1296 * keys for all possible properties.
1297 * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
1298 * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
1299 * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
1300 * invalid parameter will default to the this type as well.
1301 *
1302 * @return string|float The version of the property we are trying to extract.
1303 */
1304 public function version( $propertyName, $type = self::VERSION_TYPE_STRING ) {
1305 if ( empty( $propertyName ) ) {
1306 return false;
1307 }
1308
1309 // set the $type to the default if we don't recognize the type
1310 if ( $type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT ) {
1311 $type = self::VERSION_TYPE_STRING;
1312 }
1313
1314 $properties = self::getProperties();
1315
1316 // Check if the property exists in the properties array.
1317 if ( true === isset( $properties[ $propertyName ] ) ) {
1318
1319 // Prepare the pattern to be matched.
1320 // Make sure we always deal with an array (string is converted).
1321 $properties[ $propertyName ] = (array) $properties[ $propertyName ];
1322
1323 foreach ( $properties[ $propertyName ] as $propertyMatchString ) {
1324
1325 $propertyPattern = str_replace( '[VER]', self::VER, $propertyMatchString );
1326
1327 // Identify and extract the version.
1328 preg_match( sprintf( '#%s#is', $propertyPattern ), $this->userAgent, $match );
1329
1330 if ( false === empty( $match[1] ) ) {
1331 $version = ( $type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo( $match[1] ) : $match[1] );
1332
1333 return $version;
1334 }
1335 }
1336 }
1337
1338 return false;
1339 }
1340
1341 /**
1342 * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
1343 *
1344 * @return string One of the self::MOBILE_GRADE_* constants.
1345 */
1346 public function mobileGrade() {
1347 $isMobile = $this->isMobile();
1348
1349 if (
1350 // Apple iOS 4-7.0 – Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), iPad Retina (7.0), iPhone 3GS (4.3), iPhone 4 (4.3 / 5.1), iPhone 4S (5.1 / 6.0), iPhone 5 (6.0), and iPhone 5S (7.0)
1351 $this->is( 'iOS' ) && $this->version( 'iPad', self::VERSION_TYPE_FLOAT ) >= 4.3 ||
1352 $this->is( 'iOS' ) && $this->version( 'iPhone', self::VERSION_TYPE_FLOAT ) >= 4.3 ||
1353 $this->is( 'iOS' ) && $this->version( 'iPod', self::VERSION_TYPE_FLOAT ) >= 4.3 ||
1354
1355 // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
1356 // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
1357 // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
1358 // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
1359 ( $this->version( 'Android', self::VERSION_TYPE_FLOAT ) > 2.1 && $this->is( 'Webkit' ) ) ||
1360
1361 // Windows Phone 7.5-8 - Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia 800 (7.8), HTC Mazaa (7.8), Nokia Lumia 520 (8), Nokia Lumia 920 (8), HTC 8x (8)
1362 $this->version( 'Windows Phone OS', self::VERSION_TYPE_FLOAT ) >= 7.5 ||
1363
1364 // Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10)
1365 $this->is( 'BlackBerry' ) && $this->version( 'BlackBerry', self::VERSION_TYPE_FLOAT ) >= 6.0 ||
1366 // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
1367 $this->match( 'Playbook.*Tablet' ) ||
1368
1369 // Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0)
1370 ( $this->version( 'webOS', self::VERSION_TYPE_FLOAT ) >= 1.4 && $this->match( 'Palm|Pre|Pixi' ) ) ||
1371 // Palm WebOS 3.0 - Tested on HP TouchPad
1372 $this->match( 'hp.*TouchPad' ) ||
1373
1374 // Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices
1375 ( $this->is( 'Firefox' ) && $this->version( 'Firefox', self::VERSION_TYPE_FLOAT ) >= 18 ) ||
1376
1377 // Chrome for Android - Tested on Android 4.0, 4.1 device
1378 ( $this->is( 'Chrome' ) && $this->is( 'AndroidOS' ) && $this->version( 'Android', self::VERSION_TYPE_FLOAT ) >= 4.0 ) ||
1379
1380 // Skyfire 4.1 - Tested on Android 2.3 device
1381 ( $this->is( 'Skyfire' ) && $this->version( 'Skyfire', self::VERSION_TYPE_FLOAT ) >= 4.1 && $this->is( 'AndroidOS' ) && $this->version( 'Android', self::VERSION_TYPE_FLOAT ) >= 2.3 ) ||
1382
1383 // Opera Mobile 11.5-12: Tested on Android 2.3
1384 ( $this->is( 'Opera' ) && $this->version( 'Opera Mobi', self::VERSION_TYPE_FLOAT ) >= 11.5 && $this->is( 'AndroidOS' ) ) ||
1385
1386 // Meego 1.2 - Tested on Nokia 950 and N9
1387 $this->is( 'MeeGoOS' ) ||
1388
1389 // Sailfish OS
1390 $this->is( 'SailfishOS' ) ||
1391
1392 // Tizen (pre-release) - Tested on early hardware
1393 $this->is( 'Tizen' ) ||
1394
1395 // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
1396 // @todo: more tests here!
1397 $this->is( 'Dolfin' ) && $this->version( 'Bada', self::VERSION_TYPE_FLOAT ) >= 2.0 ||
1398
1399 // UC Browser - Tested on Android 2.3 device
1400 ( ( $this->is( 'UC Browser' ) || $this->is( 'Dolfin' ) ) && $this->version( 'Android', self::VERSION_TYPE_FLOAT ) >= 2.3 ) ||
1401
1402 // Kindle 3 and Fire - Tested on the built-in WebKit browser for each
1403 ( $this->match( 'Kindle Fire' ) ||
1404 $this->is( 'Kindle' ) && $this->version( 'Kindle', self::VERSION_TYPE_FLOAT ) >= 3.0 ) ||
1405
1406 // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
1407 $this->is( 'AndroidOS' ) && $this->is( 'NookTablet' ) ||
1408
1409 // Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7
1410 $this->version( 'Chrome', self::VERSION_TYPE_FLOAT ) >= 16 && ! $isMobile ||
1411
1412 // Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7
1413 $this->version( 'Safari', self::VERSION_TYPE_FLOAT ) >= 5.0 && ! $isMobile ||
1414
1415 // Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7
1416 $this->version( 'Firefox', self::VERSION_TYPE_FLOAT ) >= 10.0 && ! $isMobile ||
1417
1418 // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
1419 $this->version( 'IE', self::VERSION_TYPE_FLOAT ) >= 7.0 && ! $isMobile ||
1420
1421 // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
1422 $this->version( 'Opera', self::VERSION_TYPE_FLOAT ) >= 10 && ! $isMobile
1423 ) {
1424 return self::MOBILE_GRADE_A;
1425 }
1426
1427 if (
1428 $this->is( 'iOS' ) && $this->version( 'iPad', self::VERSION_TYPE_FLOAT ) < 4.3 ||
1429 $this->is( 'iOS' ) && $this->version( 'iPhone', self::VERSION_TYPE_FLOAT ) < 4.3 ||
1430 $this->is( 'iOS' ) && $this->version( 'iPod', self::VERSION_TYPE_FLOAT ) < 4.3 ||
1431
1432 // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
1433 $this->is( 'Blackberry' ) && $this->version( 'BlackBerry', self::VERSION_TYPE_FLOAT ) >= 5 && $this->version( 'BlackBerry', self::VERSION_TYPE_FLOAT ) < 6 ||
1434
1435 // Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
1436 ( $this->version( 'Opera Mini', self::VERSION_TYPE_FLOAT ) >= 5.0 && $this->version( 'Opera Mini', self::VERSION_TYPE_FLOAT ) <= 7.0 &&
1437 ( $this->version( 'Android', self::VERSION_TYPE_FLOAT ) >= 2.3 || $this->is( 'iOS' ) ) ) ||
1438
1439 // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
1440 $this->match( 'NokiaN8|NokiaC7|N97.*Series60|Symbian/3' ) ||
1441
1442 // @todo: report this (tested on Nokia N71)
1443 $this->version( 'Opera Mobi', self::VERSION_TYPE_FLOAT ) >= 11 && $this->is( 'SymbianOS' )
1444 ) {
1445 return self::MOBILE_GRADE_B;
1446 }
1447
1448 if (
1449 // Blackberry 4.x - Tested on the Curve 8330
1450 $this->version( 'BlackBerry', self::VERSION_TYPE_FLOAT ) <= 5.0 ||
1451 // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
1452 $this->match( 'MSIEMobile|Windows CE.*Mobile' ) || $this->version( 'Windows Mobile', self::VERSION_TYPE_FLOAT ) <= 5.2 ||
1453
1454 // Tested on original iPhone (3.1), iPhone 3 (3.2)
1455 $this->is( 'iOS' ) && $this->version( 'iPad', self::VERSION_TYPE_FLOAT ) <= 3.2 ||
1456 $this->is( 'iOS' ) && $this->version( 'iPhone', self::VERSION_TYPE_FLOAT ) <= 3.2 ||
1457 $this->is( 'iOS' ) && $this->version( 'iPod', self::VERSION_TYPE_FLOAT ) <= 3.2 ||
1458
1459 // Internet Explorer 7 and older - Tested on Windows XP
1460 $this->version( 'IE', self::VERSION_TYPE_FLOAT ) <= 7.0 && ! $isMobile
1461 ) {
1462 return self::MOBILE_GRADE_C;
1463 }
1464
1465 // All older smartphone platforms and featurephones - Any device that doesn't support media queries
1466 // will receive the basic, C grade experience.
1467 return self::MOBILE_GRADE_C;
1468 }
1469 }
1470