Popups, Welcome Bar, Optins and Lead Generation Plugin – Icegram - Version 1.8.6

Version Description

Fix for "white screen" errors, updated device detection, more readable call to action button colors

Download this release

Release Info

Developer niravmehta
Plugin Icon 128x128 Popups, Welcome Bar, Optins and Lead Generation Plugin – Icegram
Version 1.8.6
Comparing to
See all releases

Code changes from version 1.8.5 to 1.8.6

assets/js/icegram.js CHANGED
@@ -277,8 +277,9 @@
277
  this.el.find('.ig_button').hide();
278
  }
279
  if (this.data.bg_color != undefined && this.data.bg_color != '') {
280
- var hsl_color = window.icegram.get_complementary_color(this.data.bg_color);
281
- this.el.find('.ig_button, form input[type="submit"]').css('background', "hsl(" + hsl_color.h + "," + hsl_color.s + "%," + hsl_color.l + "%)" ).css('background-color', "hsl(" + hsl_color.h + "," + hsl_color.s + "%," + hsl_color.l + "%)" );
 
282
  }
283
  // Hint clickability for buttons / ctas
284
  if (typeof(this.data.link) === 'string' && this.data.link != '') {
@@ -428,18 +429,21 @@
428
  });
429
  }
430
 
431
- Icegram.prototype.get_complementary_color = function (hex) {
432
-
 
 
433
  hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i , function(m, r, g, b) {
434
  return r + r + g + g + b + b;
435
  });
436
 
437
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
438
  if(result){
 
439
  var r = parseInt(result[1], 16);
440
  var g = parseInt(result[2], 16);
441
  var b = parseInt(result[3], 16);
442
- var brightness = Math.sqrt(r * r * .241 + g * g * .691 + b * b * .068);
443
  r /= 255;
444
  g /= 255;
445
  b /= 255;
@@ -447,9 +451,9 @@
447
  var maxColor = Math.max(r, g, b);
448
  var minColor = Math.min(r, g, b);
449
  //Calculate L:
450
- var L = (maxColor + minColor) / 2 ;
451
- var S = 0;
452
- var H = 0;
453
  if(maxColor != minColor){
454
  //Calculate S:
455
  S = (L < 0.5) ? (maxColor - minColor) / (maxColor + minColor) : (maxColor - minColor) / (2.0 - maxColor - minColor) ;
@@ -469,17 +473,46 @@
469
  if(H<0){
470
  H += 360;
471
  }
472
- if(brightness > 130){
473
- S -= 15;
474
- L -= 25;
475
- }else{
476
- S += 15;
477
- L += 25;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  }
479
-
480
- return {h: H, s: S, l: L};
481
- } // result if
482
- return null;
 
 
483
  }
484
 
485
  if (typeof Object.create != 'function') {
277
  this.el.find('.ig_button').hide();
278
  }
279
  if (this.data.bg_color != undefined && this.data.bg_color != '') {
280
+ var hsl_colors = window.icegram.get_complementary_color(this.data.bg_color, 2);
281
+ this.el.find('.ig_button, form input[type="submit"]').css('background', "hsl(" + hsl_colors[0].h + "," + hsl_colors[0].s + "%," + hsl_colors[0].l + "%)" ).css('background-color', "hsl(" + hsl_colors[0].h + "," + hsl_colors[0].s + "%," + hsl_colors[0].l + "%)" );
282
+ this.el.find('.ig_button, form input[type="submit"]').css('color', "hsl(" + hsl_colors[1].h + "," + hsl_colors[1].s + "%," + hsl_colors[1].l + "%)" );
283
  }
284
  // Hint clickability for buttons / ctas
285
  if (typeof(this.data.link) === 'string' && this.data.link != '') {
429
  });
430
  }
431
 
432
+ Icegram.prototype.get_complementary_color = function (hex, num) {
433
+ num = num || 1;
434
+ var H, S, L, brightness;
435
+ // Convert RGB to HSL
436
  hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i , function(m, r, g, b) {
437
  return r + r + g + g + b + b;
438
  });
439
 
440
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
441
  if(result){
442
+
443
  var r = parseInt(result[1], 16);
444
  var g = parseInt(result[2], 16);
445
  var b = parseInt(result[3], 16);
446
+ brightness = Math.sqrt(r * r * .241 + g * g * .691 + b * b * .068);
447
  r /= 255;
448
  g /= 255;
449
  b /= 255;
451
  var maxColor = Math.max(r, g, b);
452
  var minColor = Math.min(r, g, b);
453
  //Calculate L:
454
+ L = (maxColor + minColor) / 2 ;
455
+ S = 0;
456
+ H = 0;
457
  if(maxColor != minColor){
458
  //Calculate S:
459
  S = (L < 0.5) ? (maxColor - minColor) / (maxColor + minColor) : (maxColor - minColor) / (2.0 - maxColor - minColor) ;
473
  if(H<0){
474
  H += 360;
475
  }
476
+ }
477
+
478
+ // Now change HSL to vary color
479
+ var colors = [];
480
+
481
+ // Increase or reduce saturation / light
482
+ if(brightness > 146){
483
+ L = Math.max( L-25, 0);
484
+ S = Math.min( S-15, 100);
485
+ }else{
486
+ L = Math.min( L+25, 100);
487
+ S = Math.min( S+15, 100);
488
+ }
489
+
490
+ for(var i=0; i < num; i=i+2) {
491
+
492
+ // First create background color
493
+ H += 45;
494
+ if (H > 360) {
495
+ H -= 180;
496
+ }
497
+
498
+ colors.push({h: H, s: S, l: L});
499
+
500
+ // Now, create foreground color if needed
501
+ if (i+1 < num) {
502
+ var fg = {};
503
+ if(L > 50){
504
+ fg = {h: H, s: 20, l: 20 };
505
+ }else{
506
+ colors.push({h: H, s: 20, l: 90 });
507
+ }
508
+ colors.push(fg);
509
  }
510
+ }
511
+
512
+ if (colors.length > 0) {
513
+ return (num == 1) ? colors[0] : colors.slice(0, num);
514
+ }
515
+ return null;
516
  }
517
 
518
  if (typeof Object.create != 'function') {
classes/class-icegram-campaign-admin.php CHANGED
@@ -14,7 +14,7 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
14
  add_action( 'save_post', array( &$this, 'save_campaign_settings' ), 10, 2 );
15
  add_action( 'wp_ajax_icegram_json_search_messages', array( &$this, 'icegram_json_search_messages' ) );
16
  add_action( 'wp_ajax_get_message_action_row', array( &$this, 'get_message_action_row' ) );
17
- //add_filter( 'wp_default_editor', create_function('', 'return "html";') );
18
  add_action( 'wp_ajax_save_campaign_preview', array( &$this, 'save_campaign_preview' ) );
19
  add_action( 'icegram_campaign_target_rules', array( &$this, 'icegram_add_campaign_target_rules' ), 10, 2 );
20
  add_filter('icegram_campaign_messages' ,array( &$this, 'get_icegram_campaign_messages' ) ,10,2 );
@@ -657,7 +657,7 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
657
  }
658
 
659
  function duplicate_campaign(){
660
- if($_REQUEST['action'] == 'duplicate-campaign' && !empty($_REQUEST['campaign_id'])){
661
  Icegram::duplicate( $_REQUEST['campaign_id'] );
662
  }
663
  }
14
  add_action( 'save_post', array( &$this, 'save_campaign_settings' ), 10, 2 );
15
  add_action( 'wp_ajax_icegram_json_search_messages', array( &$this, 'icegram_json_search_messages' ) );
16
  add_action( 'wp_ajax_get_message_action_row', array( &$this, 'get_message_action_row' ) );
17
+ // add_filter( 'wp_default_editor', create_function('', 'return "html";') );
18
  add_action( 'wp_ajax_save_campaign_preview', array( &$this, 'save_campaign_preview' ) );
19
  add_action( 'icegram_campaign_target_rules', array( &$this, 'icegram_add_campaign_target_rules' ), 10, 2 );
20
  add_filter('icegram_campaign_messages' ,array( &$this, 'get_icegram_campaign_messages' ) ,10,2 );
657
  }
658
 
659
  function duplicate_campaign(){
660
+ if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'duplicate-campaign' && !empty($_REQUEST['campaign_id'])){
661
  Icegram::duplicate( $_REQUEST['campaign_id'] );
662
  }
663
  }
classes/class-icegram-message-admin.php CHANGED
@@ -247,7 +247,8 @@ if ( !class_exists( 'Icegram_Message_Admin' ) ) {
247
  <strong>'.__('Target Link' ,'icegram').'</strong>
248
  <span class="help_tip admin_field_icon" data-tip='.__("Enter destination URL here. Clicking will redirect to this link." ,'icegram').'></span>
249
  </label>
250
- <input type="text" class="message_field" name="message_data['.$message_id.'][link]" id="message_link" value="'.esc_attr( $message_data['link'] ) .'" />
 
251
  </p>';
252
 
253
  $icegram_message_target_link = apply_filters('icegram_message_field_link' , array( 'html' => $target_link_field ,'message_id' => $message_id ,'message_data' => $message_data) );
@@ -298,6 +299,10 @@ if ( !class_exists( 'Icegram_Message_Admin' ) ) {
298
  <?php $message = ( !empty( $message_data['message'] ) ) ? $message_data['message'] : ''; ?>
299
  <?php wp_editor( $message, 'edit'.$message_id, $editor_args ); ?>
300
  </p>
 
 
 
 
301
  <p class="message_row position <?php echo implode( ' ', $settings['position'] )?>">
302
  <label for="message_position" class="message_label"><strong><?php _e( 'Position', 'icegram' ); ?></strong></label>
303
  <span class="message_field location-selector message_label">
@@ -596,7 +601,7 @@ if ( !class_exists( 'Icegram_Message_Admin' ) ) {
596
  }
597
 
598
  function duplicate_message(){
599
- if($_REQUEST['action'] == 'duplicate-message' && !empty($_REQUEST['message_id'])){
600
  Icegram::duplicate( $_REQUEST['message_id'] );
601
  }
602
  }
247
  <strong>'.__('Target Link' ,'icegram').'</strong>
248
  <span class="help_tip admin_field_icon" data-tip='.__("Enter destination URL here. Clicking will redirect to this link." ,'icegram').'></span>
249
  </label>
250
+ <input type="text" class="message_field" name="message_data['.$message_id.'][link]"
251
+ id="message_link" value="'.esc_attr( ( isset($message_data['link']) ? $message_data['link'] : '') ) .'" />
252
  </p>';
253
 
254
  $icegram_message_target_link = apply_filters('icegram_message_field_link' , array( 'html' => $target_link_field ,'message_id' => $message_id ,'message_data' => $message_data) );
299
  <?php $message = ( !empty( $message_data['message'] ) ) ? $message_data['message'] : ''; ?>
300
  <?php wp_editor( $message, 'edit'.$message_id, $editor_args ); ?>
301
  </p>
302
+ <?php
303
+ // action add for design studio
304
+ do_action( 'icegram_after_message_body', $message_id, $message_data );
305
+ ?>
306
  <p class="message_row position <?php echo implode( ' ', $settings['position'] )?>">
307
  <label for="message_position" class="message_label"><strong><?php _e( 'Position', 'icegram' ); ?></strong></label>
308
  <span class="message_field location-selector message_label">
601
  }
602
 
603
  function duplicate_message(){
604
+ if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'duplicate-message' && !empty($_REQUEST['message_id'])){
605
  Icegram::duplicate( $_REQUEST['message_id'] );
606
  }
607
  }
classes/ig-mobile-detect.php ADDED
@@ -0,0 +1,1344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Mobile Detect Library
4
+ * =====================
5
+ *
6
+ * Motto: "Every business should have a mobile detection script to detect mobile readers"
7
+ *
8
+ * Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
9
+ * It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
10
+ *
11
+ * @author Current authors: Serban Ghita <serbanghita@gmail.com>
12
+ * Nick Ilyin <nick.ilyin@gmail.com>
13
+ *
14
+ * Original author: Victor Stanciu <vic.stanciu@gmail.com>
15
+ *
16
+ * @license Code and contributions have 'MIT License'
17
+ * More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
18
+ *
19
+ * @link Homepage: http://mobiledetect.net
20
+ * GitHub Repo: https://github.com/serbanghita/Mobile-Detect
21
+ * Google Code: http://code.google.com/p/php-mobile-detect/
22
+ * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md
23
+ * HOWTO: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
24
+ *
25
+ * @version 2.8.11
26
+ */
27
+ if ( !class_exists( 'Ig_Mobile_Detect' ) )
28
+ {
29
+
30
+ class Ig_Mobile_Detect
31
+ {
32
+ /**
33
+ * Mobile detection type.
34
+ *
35
+ * @deprecated since version 2.6.9
36
+ */
37
+ const DETECTION_TYPE_MOBILE = 'mobile';
38
+
39
+ /**
40
+ * Extended detection type.
41
+ *
42
+ * @deprecated since version 2.6.9
43
+ */
44
+ const DETECTION_TYPE_EXTENDED = 'extended';
45
+
46
+ /**
47
+ * A frequently used regular expression to extract version #s.
48
+ *
49
+ * @deprecated since version 2.6.9
50
+ */
51
+ const VER = '([\w._\+]+)';
52
+
53
+ /**
54
+ * Top-level device.
55
+ */
56
+ const MOBILE_GRADE_A = 'A';
57
+
58
+ /**
59
+ * Mid-level device.
60
+ */
61
+ const MOBILE_GRADE_B = 'B';
62
+
63
+ /**
64
+ * Low-level device.
65
+ */
66
+ const MOBILE_GRADE_C = 'C';
67
+
68
+ /**
69
+ * Stores the version number of the current release.
70
+ */
71
+ const VERSION = '2.8.11';
72
+
73
+ /**
74
+ * A type for the version() method indicating a string return value.
75
+ */
76
+ const VERSION_TYPE_STRING = 'text';
77
+
78
+ /**
79
+ * A type for the version() method indicating a float return value.
80
+ */
81
+ const VERSION_TYPE_FLOAT = 'float';
82
+
83
+ /**
84
+ * The User-Agent HTTP header is stored in here.
85
+ * @var string
86
+ */
87
+ protected $userAgent = null;
88
+
89
+ /**
90
+ * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
91
+ * @var array
92
+ */
93
+ protected $httpHeaders = array();
94
+
95
+ /**
96
+ * The matching Regex.
97
+ * This is good for debug.
98
+ * @var string
99
+ */
100
+ protected $matchingRegex = null;
101
+
102
+ /**
103
+ * The matches extracted from the regex expression.
104
+ * This is good for debug.
105
+ * @var string
106
+ */
107
+ protected $matchesArray = null;
108
+
109
+ /**
110
+ * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED.
111
+ *
112
+ * @deprecated since version 2.6.9
113
+ *
114
+ * @var string
115
+ */
116
+ protected $detectionType = self::DETECTION_TYPE_MOBILE;
117
+
118
+ /**
119
+ * HTTP headers that trigger the 'isMobile' detection
120
+ * to be true.
121
+ *
122
+ * @var array
123
+ */
124
+ protected static $mobileHeaders = array(
125
+
126
+ 'HTTP_ACCEPT' => array('matches' => array(
127
+ // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
128
+ 'application/x-obml2d',
129
+ // BlackBerry devices.
130
+ 'application/vnd.rim.html',
131
+ 'text/vnd.wap.wml',
132
+ 'application/vnd.wap.xhtml+xml'
133
+ )),
134
+ 'HTTP_X_WAP_PROFILE' => null,
135
+ 'HTTP_X_WAP_CLIENTID' => null,
136
+ 'HTTP_WAP_CONNECTION' => null,
137
+ 'HTTP_PROFILE' => null,
138
+ // Reported by Opera on Nokia devices (eg. C3).
139
+ 'HTTP_X_OPERAMINI_PHONE_UA' => null,
140
+ 'HTTP_X_NOKIA_GATEWAY_ID' => null,
141
+ 'HTTP_X_ORANGE_ID' => null,
142
+ 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
143
+ 'HTTP_X_HUAWEI_USERID' => null,
144
+ // Reported by Windows Smartphones.
145
+ 'HTTP_UA_OS' => null,
146
+ // Reported by Verizon, Vodafone proxy system.
147
+ 'HTTP_X_MOBILE_GATEWAY' => null,
148
+ // Seen this on HTC Sensation. SensationXE_Beats_Z715e.
149
+ 'HTTP_X_ATT_DEVICEID' => null,
150
+ // Seen this on a HTC.
151
+ 'HTTP_UA_CPU' => array('matches' => array('ARM')),
152
+ );
153
+
154
+ /**
155
+ * List of mobile devices (phones).
156
+ *
157
+ * @var array
158
+ */
159
+ protected static $phoneDevices = array(
160
+ 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
161
+ 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+',
162
+ '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',
163
+ 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6',
164
+ // @todo: Is 'Dell Streak' a tablet or a phone? ;)
165
+ 'Dell' => 'Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
166
+ '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',
167
+ 'Samsung' => 'Samsung|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',
168
+ '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)',
169
+ 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533',
170
+ 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile',
171
+ // http://www.micromaxinfo.com/mobiles/smartphones
172
+ // Added because the codes might conflict with Acer Tablets.
173
+ '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',
174
+ // @todo Complete the regex.
175
+ 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
176
+ 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
177
+ // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
178
+ // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
179
+ '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',
180
+ // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
181
+ 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
182
+ 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
183
+ // Added simvalley mobile just for fun. They have some interesting devices.
184
+ // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
185
+ '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',
186
+ // Wolfgang - a brand that is sold by Aldi supermarkets.
187
+ // http://www.wolfgangmobile.com/
188
+ 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
189
+ 'Alcatel' => 'Alcatel',
190
+ 'Nintendo' => 'Nintendo 3DS',
191
+ // http://en.wikipedia.org/wiki/Amoi
192
+ 'Amoi' => 'Amoi',
193
+ // http://en.wikipedia.org/wiki/INQ
194
+ 'INQ' => 'INQ',
195
+ // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
196
+ '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',
197
+ );
198
+
199
+ /**
200
+ * List of tablet devices.
201
+ *
202
+ * @var array
203
+ */
204
+ protected static $tabletDevices = array(
205
+ 'iPad' => 'iPad|iPad.*Mobile', // @todo: check for mobile friendly emails topic.
206
+ 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)|^.*Android.*Nexus(?:(?!Mobile).)*$',
207
+ '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-I9205|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-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T707A|SM-T807A|SM-T237P|SM-T807P|SM-P607T|SM-T217T|SM-T337T', // SCH-P709|SCH-P729|SM-T2558 - Samsung Mega - treat them like a regular phone.
208
+ // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
209
+ 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI)\b',
210
+ // Only the Surface tablets with Windows RT are considered mobile.
211
+ // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
212
+ 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
213
+ // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
214
+ 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
215
+ // Watch out for PadFone, see #132.
216
+ // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
217
+ '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',
218
+ 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
219
+ 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
220
+ 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
221
+ 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
222
+ // http://www.acer.ro/ac/ro/RO/content/drivers
223
+ // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
224
+ // http://us.acer.com/ac/en/US/content/group/tablets
225
+ // http://www.acer.de/ac/de/DE/content/models/tablets/
226
+ // Can conflict with Micromax and Motorola phones codes.
227
+ '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',
228
+ // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
229
+ // http://us.toshiba.com/tablets/tablet-finder
230
+ // http://www.toshiba.co.jp/regza/tablet/
231
+ 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
232
+ // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
233
+ // http://www.lg.com/us/tablets
234
+ 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
235
+ 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
236
+ // Prestigio Tablets http://www.prestigio.com/support
237
+ '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',
238
+ // http://support.lenovo.com/en_GB/downloads/default.page?#
239
+ 'LenovoTablet' => 'Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)',
240
+ // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
241
+ 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
242
+ // http://www.yarvik.com/en/matrix/tablets/
243
+ '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',
244
+ 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
245
+ 'ArnovaTablet' => 'AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
246
+ // http://www.intenso.de/kategorie_en.php?kategorie=33
247
+ // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
248
+ 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
249
+ // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
250
+ 'IRUTablet' => 'M702pro',
251
+ 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
252
+ // http://www.e-boda.ro/tablete-pc.html
253
+ 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
254
+ // http://www.allview.ro/produse/droseries/lista-tablete-pc/
255
+ 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
256
+ // http://wiki.archosfans.com/index.php?title=Main_Page
257
+ 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
258
+ // http://www.ainol.com/plugin.php?identifier=ainol&module=product
259
+ 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
260
+ // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
261
+ // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
262
+ // http://www.sony.jp/support/tablet/
263
+ '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',
264
+ // 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
265
+ 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
266
+ // db + http://www.cube-tablet.com/buy-products.html
267
+ 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
268
+ // http://www.cobyusa.com/?p=pcat&pcat_id=3001
269
+ 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
270
+ // http://www.match.net.cn/products.asp
271
+ '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',
272
+ // http://www.msi.com/support
273
+ // @todo Research the Windows Tablets.
274
+ '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',
275
+ // @todo http://www.kyoceramobile.com/support/drivers/
276
+ // 'KyoceraTablet' => null,
277
+ // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
278
+ // 'IntextTablet' => null,
279
+ // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
280
+ // http://www.imp3.net/14/show.php?itemid=20454
281
+ 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
282
+ // http://www.rock-chips.com/index.php?do=prod&pid=2
283
+ 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
284
+ // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
285
+ 'FlyTablet' => 'IQ310|Fly Vision',
286
+ // http://www.bqreaders.com/gb/tablets-prices-sale.html
287
+ 'bqTablet' => 'bq.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant)|Maxwell.*Lite|Maxwell.*Plus',
288
+ // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
289
+ // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
290
+ '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',
291
+ // Nec or Medias Tab
292
+ 'NecTablet' => '\bN-06D|\bN-08D',
293
+ // Pantech Tablets: http://www.pantechusa.com/phones/
294
+ 'PantechTablet' => 'Pantech.*P4100',
295
+ // Broncho Tablets: http://www.broncho.cn/ (hard to find)
296
+ 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
297
+ // http://versusuk.com/support.html
298
+ 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
299
+ // http://www.zync.in/index.php/our-products/tablet-phablets
300
+ 'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900',
301
+ // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
302
+ 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
303
+ // https://www.nabitablet.com/
304
+ 'NabiTablet' => 'Android.*\bNabi',
305
+ 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
306
+ // French Danew Tablets http://www.danew.com/produits-tablette.php
307
+ 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
308
+ // Texet Tablets and Readers http://www.texet.ru/tablet/
309
+ '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',
310
+ // Avoid detecting 'PLAYSTATION 3' as mobile.
311
+ 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
312
+ // http://www.trekstor.de/surftabs.html
313
+ 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
314
+ // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
315
+ 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
316
+ // http://www.advandigital.com/index.php?link=content-product&jns=JP001
317
+ // because of the short codenames we have to include whitespaces to reduce the possible conflicts.
318
+ '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 ',
319
+ // http://www.danytech.com/category/tablet-pc
320
+ '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',
321
+ // http://www.galapad.net/product.html
322
+ 'GalapadTablet' => 'Android.*\bG1\b',
323
+ // http://www.micromaxinfo.com/tablet/funbook
324
+ 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
325
+ // http://www.karbonnmobiles.com/products_tablet.php
326
+ 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
327
+ // http://www.myallfine.com/Products.asp
328
+ 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
329
+ // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
330
+ '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',
331
+ // http://www.yonesnav.com/products/products.php
332
+ 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
333
+ // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
334
+ // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
335
+ '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',
336
+ // http://www.gloryunion.cn/products.asp
337
+ // http://www.allwinnertech.com/en/apply/mobile.html
338
+ // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
339
+ // @todo: Softwiner tablets?
340
+ // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
341
+ '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
342
+ // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
343
+ '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',
344
+ // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
345
+ // @todo: add more tests.
346
+ 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)',
347
+ // http://hclmetablet.com/India/index.php
348
+ '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',
349
+ // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
350
+ 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
351
+ // http://www.visture.com/index.asp
352
+ 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
353
+ // http://www.mijncresta.nl/tablet
354
+ 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
355
+ // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
356
+ 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
357
+ // Concorde tab
358
+ 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
359
+ // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
360
+ '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',
361
+ // Modecom Tablets - http://www.modecom.eu/tablets/portal/
362
+ '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',
363
+ // Vonino Tablets - http://www.vonino.eu/tablets
364
+ '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',
365
+ // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
366
+ 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
367
+ // Storex Tablets - http://storex.fr/espace_client/support.html
368
+ // @note: no need to add all the tablet codes since they are guided by the first regex.
369
+ 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
370
+ // Generic Vodafone tablets.
371
+ 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7',
372
+ // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
373
+ // Aka: http://www.essentielb.fr/
374
+ 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
375
+ // Ross & Moor - http://ross-moor.ru/
376
+ 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
377
+ // i-mobile http://product.i-mobilephone.com/Mobile_Device
378
+ 'iMobileTablet' => 'i-mobile i-note',
379
+ // http://www.tolino.de/de/vergleichen/
380
+ 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
381
+ // AudioSonic - a Kmart brand
382
+ // 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
383
+ 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
384
+ // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
385
+ // @todo: add them gradually to avoid conflicts.
386
+ 'AMPETablet' => 'Android.* A78 ',
387
+ // Skk Mobile - http://skkmobile.com.ph/product_tablets.php
388
+ 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
389
+ // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
390
+ 'TecnoTablet' => 'TECNO P9',
391
+ // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
392
+ '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',
393
+ // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
394
+ '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)',
395
+ // http://www.intracon.eu/tablet
396
+ 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
397
+ // http://www.xoro.de/produkte/
398
+ // @note: Might be the same brand with 'Simply tablets'
399
+ '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',
400
+ // http://www1.viewsonic.com/products/computing/tablets/
401
+ 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
402
+ // http://www.odys.de/web/internet-tablet_en.html
403
+ 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
404
+ // http://www.captiva-power.de/products.html#tablets-en
405
+ 'CaptivaTablet' => 'CAPTIVA PAD',
406
+ // IconBIT - http://www.iconbit.com/products/tablets/
407
+ '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',
408
+ // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
409
+ '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',
410
+ 'JaytechTablet' => 'TPC-PA762',
411
+ 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
412
+ // http://www.digma.ru/support/download/
413
+ // @todo: Ebooks also (if requested)
414
+ 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
415
+ // http://www.evolioshop.com/ro/tablete-pc.html
416
+ // http://www.evolio.ro/support/downloads_static.html?cat=2
417
+ // @todo: Research some more
418
+ 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
419
+ // @todo http://www.lavamobiles.com/tablets-data-cards
420
+ 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY',
421
+ // https://www.celkonmobiles.com/?_a=categoryphones&sid=2
422
+ 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
423
+ // http://www.mi.com/en
424
+ 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
425
+ // http://www.nbru.cn/index.html
426
+ 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
427
+ // http://navroad.com/products/produkty/tablety/
428
+ 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
429
+ // http://www.datawind.com/ubislate/
430
+ 'UbislateTablet' => 'UbiSlate[\s]?7C',
431
+ // http://www.pocketbook-int.com/ru/support
432
+ 'PocketBookTablet' => 'Pocketbook',
433
+ // http://www.tesco.com/direct/hudl/
434
+ 'Hudl' => 'Hudl HT7S3',
435
+ // http://www.telstra.com.au/home-phone/thub-2/
436
+ 'TelstraTablet' => 'T-Hub2',
437
+ '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|\bJolla\b'
438
+ );
439
+
440
+ /**
441
+ * List of mobile Operating Systems.
442
+ *
443
+ * @var array
444
+ */
445
+ protected static $operatingSystems = array(
446
+ 'AndroidOS' => 'Android',
447
+ 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
448
+ 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
449
+ 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
450
+ // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
451
+ 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;',
452
+ // @reference: http://en.wikipedia.org/wiki/Windows_Phone
453
+ // http://wifeng.cn/?r=blog&a=view&id=106
454
+ // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
455
+ // http://msdn.microsoft.com/library/ms537503.aspx
456
+ 'WindowsPhoneOS' => 'Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
457
+ 'iOS' => '\biPhone.*Mobile|\biPod|\biPad',
458
+ // http://en.wikipedia.org/wiki/MeeGo
459
+ // @todo: research MeeGo in UAs
460
+ 'MeeGoOS' => 'MeeGo',
461
+ // http://en.wikipedia.org/wiki/Maemo
462
+ // @todo: research Maemo in UAs
463
+ 'MaemoOS' => 'Maemo',
464
+ 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
465
+ 'webOS' => 'webOS|hpwOS',
466
+ 'badaOS' => '\bBada\b',
467
+ 'BREWOS' => 'BREW',
468
+ );
469
+
470
+ /**
471
+ * List of mobile User Agents.
472
+ *
473
+ * @var array
474
+ */
475
+ protected static $browsers = array(
476
+ // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
477
+ 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?',
478
+ 'Dolfin' => '\bDolfin\b',
479
+ 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+',
480
+ 'Skyfire' => 'Skyfire',
481
+ 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
482
+ 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile',
483
+ 'Bolt' => 'bolt',
484
+ 'TeaShark' => 'teashark',
485
+ 'Blazer' => 'Blazer',
486
+ // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
487
+ 'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari',
488
+ // http://en.wikipedia.org/wiki/Midori_(web_browser)
489
+ //'Midori' => 'midori',
490
+ 'Tizen' => 'Tizen',
491
+ 'UCBrowser' => 'UC.*Browser|UCWEB',
492
+ 'baiduboxapp' => 'baiduboxapp',
493
+ 'baidubrowser' => 'baidubrowser',
494
+ // https://github.com/serbanghita/Mobile-Detect/issues/7
495
+ 'DiigoBrowser' => 'DiigoBrowser',
496
+ // http://www.puffinbrowser.com/index.php
497
+ 'Puffin' => 'Puffin',
498
+ // http://mercury-browser.com/index.html
499
+ 'Mercury' => '\bMercury\b',
500
+ // http://en.wikipedia.org/wiki/Obigo_Browser
501
+ 'ObigoBrowser' => 'Obigo',
502
+ // http://en.wikipedia.org/wiki/NetFront
503
+ 'NetFront' => 'NF-Browser',
504
+ // @reference: http://en.wikipedia.org/wiki/Minimo
505
+ // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
506
+ 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
507
+ );
508
+
509
+ /**
510
+ * Utilities.
511
+ *
512
+ * @var array
513
+ */
514
+ protected static $utilities = array(
515
+ // Experimental. When a mobile device wants to switch to 'Desktop Mode'.
516
+ // http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
517
+ // https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
518
+ 'DesktopMode' => 'WPDesktop',
519
+ 'TV' => 'SonyDTV|HbbTV', // experimental
520
+ 'WebKit' => '(webkit)[ /]([\w.]+)',
521
+ 'Bot' => 'Googlebot|YandexBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|facebookexternalhit',
522
+ 'MobileBot' => 'Googlebot-Mobile|YahooSeeker/M1A1-R2D2',
523
+ // @todo: Include JXD consoles.
524
+ 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\b',
525
+ 'Watch' => 'SM-V700',
526
+ );
527
+
528
+ /**
529
+ * All possible HTTP headers that represent the
530
+ * User-Agent string.
531
+ *
532
+ * @var array
533
+ */
534
+ protected static $uaHttpHeaders = array(
535
+ // The default User-Agent string.
536
+ 'HTTP_USER_AGENT',
537
+ // Header can occur on devices using Opera Mini.
538
+ 'HTTP_X_OPERAMINI_PHONE_UA',
539
+ // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
540
+ 'HTTP_X_DEVICE_USER_AGENT',
541
+ 'HTTP_X_ORIGINAL_USER_AGENT',
542
+ 'HTTP_X_SKYFIRE_PHONE',
543
+ 'HTTP_X_BOLT_PHONE_UA',
544
+ 'HTTP_DEVICE_STOCK_UA',
545
+ 'HTTP_X_UCBROWSER_DEVICE_UA'
546
+ );
547
+
548
+ /**
549
+ * The individual segments that could exist in a User-Agent string. VER refers to the regular
550
+ * expression defined in the constant self::VER.
551
+ *
552
+ * @var array
553
+ */
554
+ protected static $properties = array(
555
+
556
+ // Build
557
+ 'Mobile' => 'Mobile/[VER]',
558
+ 'Build' => 'Build/[VER]',
559
+ 'Version' => 'Version/[VER]',
560
+ 'VendorID' => 'VendorID/[VER]',
561
+
562
+ // Devices
563
+ 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
564
+ 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
565
+ 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
566
+ //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
567
+ 'Kindle' => 'Kindle/[VER]',
568
+
569
+ // Browser
570
+ 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
571
+ 'Coast' => array('Coast/[VER]'),
572
+ 'Dolfin' => 'Dolfin/[VER]',
573
+ // @reference: https://developer.mozilla.org/en-US/docs/User_Agent_Strings_Reference
574
+ 'Firefox' => 'Firefox/[VER]',
575
+ 'Fennec' => 'Fennec/[VER]',
576
+ // @reference: http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
577
+ 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];'),
578
+ // http://en.wikipedia.org/wiki/NetFront
579
+ 'NetFront' => 'NetFront/[VER]',
580
+ 'NokiaBrowser' => 'NokiaBrowser/[VER]',
581
+ 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
582
+ 'Opera Mini' => 'Opera Mini/[VER]',
583
+ 'Opera Mobi' => 'Version/[VER]',
584
+ 'UC Browser' => 'UC Browser[VER]',
585
+ 'MQQBrowser' => 'MQQBrowser/[VER]',
586
+ 'MicroMessenger' => 'MicroMessenger/[VER]',
587
+ 'baiduboxapp' => 'baiduboxapp/[VER]',
588
+ 'baidubrowser' => 'baidubrowser/[VER]',
589
+ 'Iron' => 'Iron/[VER]',
590
+ // @note: Safari 7534.48.3 is actually Version 5.1.
591
+ // @note: On BlackBerry the Version is overwriten by the OS.
592
+ 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
593
+ 'Skyfire' => 'Skyfire/[VER]',
594
+ 'Tizen' => 'Tizen/[VER]',
595
+ 'Webkit' => 'webkit[ /][VER]',
596
+
597
+ // Engine
598
+ 'Gecko' => 'Gecko/[VER]',
599
+ 'Trident' => 'Trident/[VER]',
600
+ 'Presto' => 'Presto/[VER]',
601
+
602
+ // OS
603
+ 'iOS' => ' \bOS\b [VER] ',
604
+ 'Android' => 'Android [VER]',
605
+ 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
606
+ 'BREW' => 'BREW [VER]',
607
+ 'Java' => 'Java/[VER]',
608
+ // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
609
+ // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
610
+ 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'),
611
+ 'Windows Phone' => 'Windows Phone [VER]',
612
+ 'Windows CE' => 'Windows CE/[VER]',
613
+ // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
614
+ 'Windows NT' => 'Windows NT [VER]',
615
+ 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
616
+ 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'),
617
+ );
618
+
619
+ /**
620
+ * Construct an instance of this class.
621
+ *
622
+ * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored.
623
+ * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
624
+ * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
625
+ * from the $headers array instead.
626
+ */
627
+ public function __construct(
628
+ array $headers = null,
629
+ $userAgent = null
630
+ ) {
631
+ $this->setHttpHeaders($headers);
632
+ $this->setUserAgent($userAgent);
633
+ }
634
+
635
+ /**
636
+ * Get the current script version.
637
+ * This is useful for the demo.php file,
638
+ * so people can check on what version they are testing
639
+ * for mobile devices.
640
+ *
641
+ * @return string The version number in semantic version format.
642
+ */
643
+ public static function getScriptVersion()
644
+ {
645
+ return self::VERSION;
646
+ }
647
+
648
+ /**
649
+ * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
650
+ *
651
+ * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
652
+ * the headers. The default null is left for backwards compatibilty.
653
+ */
654
+ public function setHttpHeaders($httpHeaders = null)
655
+ {
656
+ //use global _SERVER if $httpHeaders aren't defined
657
+ if (!is_array($httpHeaders) || !count($httpHeaders)) {
658
+ $httpHeaders = $_SERVER;
659
+ }
660
+
661
+ //clear existing headers
662
+ $this->httpHeaders = array();
663
+
664
+ //Only save HTTP headers. In PHP land, that means only _SERVER vars that
665
+ //start with HTTP_.
666
+ foreach ($httpHeaders as $key => $value) {
667
+ if (substr($key,0,5) == 'HTTP_') {
668
+ $this->httpHeaders[$key] = $value;
669
+ }
670
+ }
671
+ }
672
+
673
+ /**
674
+ * Retrieves the HTTP headers.
675
+ *
676
+ * @return array
677
+ */
678
+ public function getHttpHeaders()
679
+ {
680
+ return $this->httpHeaders;
681
+ }
682
+
683
+ /**
684
+ * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
685
+ * Simply null is returned.
686
+ *
687
+ * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
688
+ * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
689
+ * all-caps, HTTP_ prefixed, underscore seperated awesomeness.
690
+ *
691
+ * @return string|null The value of the header.
692
+ */
693
+ public function getHttpHeader($header)
694
+ {
695
+ //are we using PHP-flavored headers?
696
+ if (strpos($header, '_') === false) {
697
+ $header = str_replace('-', '_', $header);
698
+ $header = strtoupper($header);
699
+ }
700
+
701
+ //test the alternate, too
702
+ $altHeader = 'HTTP_' . $header;
703
+
704
+ //Test both the regular and the HTTP_ prefix
705
+ if (isset($this->httpHeaders[$header])) {
706
+ return $this->httpHeaders[$header];
707
+ } elseif (isset($this->httpHeaders[$altHeader])) {
708
+ return $this->httpHeaders[$altHeader];
709
+ }
710
+
711
+ return null;
712
+ }
713
+
714
+ public function getMobileHeaders()
715
+ {
716
+ return self::$mobileHeaders;
717
+ }
718
+
719
+ /**
720
+ * Get all possible HTTP headers that
721
+ * can contain the User-Agent string.
722
+ *
723
+ * @return array List of HTTP headers.
724
+ */
725
+ public function getUaHttpHeaders()
726
+ {
727
+ return self::$uaHttpHeaders;
728
+ }
729
+
730
+ /**
731
+ * Set the User-Agent to be used.
732
+ *
733
+ * @param string $userAgent The user agent string to set.
734
+ *
735
+ * @return string|null
736
+ */
737
+ public function setUserAgent($userAgent = null)
738
+ {
739
+ if (!empty($userAgent)) {
740
+ return $this->userAgent = $userAgent;
741
+ } else {
742
+ $this->userAgent = null;
743
+ foreach ($this->getUaHttpHeaders() as $altHeader) {
744
+ if (!empty($this->httpHeaders[$altHeader])) { // @todo: should use getHttpHeader(), but it would be slow. (Serban)
745
+ $this->userAgent .= $this->httpHeaders[$altHeader] . " ";
746
+ }
747
+ }
748
+
749
+ return $this->userAgent = (!empty($this->userAgent) ? trim($this->userAgent) : null);
750
+
751
+ }
752
+ }
753
+
754
+ /**
755
+ * Retrieve the User-Agent.
756
+ *
757
+ * @return string|null The user agent if it's set.
758
+ */
759
+ public function getUserAgent()
760
+ {
761
+ return $this->userAgent;
762
+ }
763
+
764
+ /**
765
+ * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or
766
+ * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set.
767
+ *
768
+ * @deprecated since version 2.6.9
769
+ *
770
+ * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default
771
+ * parameter is null which will default to self::DETECTION_TYPE_MOBILE.
772
+ */
773
+ public function setDetectionType($type = null)
774
+ {
775
+ if ($type === null) {
776
+ $type = self::DETECTION_TYPE_MOBILE;
777
+ }
778
+
779
+ if ($type != self::DETECTION_TYPE_MOBILE && $type != self::DETECTION_TYPE_EXTENDED) {
780
+ return;
781
+ }
782
+
783
+ $this->detectionType = $type;
784
+ }
785
+
786
+ public function getMatchingRegex()
787
+ {
788
+ return $this->matchingRegex;
789
+ }
790
+
791
+ public function getMatchesArray()
792
+ {
793
+ return $this->matchesArray;
794
+ }
795
+
796
+ /**
797
+ * Retrieve the list of known phone devices.
798
+ *
799
+ * @return array List of phone devices.
800
+ */
801
+ public static function getPhoneDevices()
802
+ {
803
+ return self::$phoneDevices;
804
+ }
805
+
806
+ /**
807
+ * Retrieve the list of known tablet devices.
808
+ *
809
+ * @return array List of tablet devices.
810
+ */
811
+ public static function getTabletDevices()
812
+ {
813
+ return self::$tabletDevices;
814
+ }
815
+
816
+ /**
817
+ * Alias for getBrowsers() method.
818
+ *
819
+ * @return array List of user agents.
820
+ */
821
+ public static function getUserAgents()
822
+ {
823
+ return self::getBrowsers();
824
+ }
825
+
826
+ /**
827
+ * Retrieve the list of known browsers. Specifically, the user agents.
828
+ *
829
+ * @return array List of browsers / user agents.
830
+ */
831
+ public static function getBrowsers()
832
+ {
833
+ return self::$browsers;
834
+ }
835
+
836
+ /**
837
+ * Retrieve the list of known utilities.
838
+ *
839
+ * @return array List of utilities.
840
+ */
841
+ public static function getUtilities()
842
+ {
843
+ return self::$utilities;
844
+ }
845
+
846
+ /**
847
+ * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
848
+ *
849
+ * @deprecated since version 2.6.9
850
+ *
851
+ * @return array All the rules (but not extended).
852
+ */
853
+ public static function getMobileDetectionRules()
854
+ {
855
+ static $rules;
856
+
857
+ if (!$rules) {
858
+ $rules = array_merge(
859
+ self::$phoneDevices,
860
+ self::$tabletDevices,
861
+ self::$operatingSystems,
862
+ self::$browsers
863
+ );
864
+ }
865
+
866
+ return $rules;
867
+
868
+ }
869
+
870
+ /**
871
+ * Method gets the mobile detection rules + utilities.
872
+ * The reason this is separate is because utilities rules
873
+ * don't necessary imply mobile. This method is used inside
874
+ * the new $detect->is('stuff') method.
875
+ *
876
+ * @deprecated since version 2.6.9
877
+ *
878
+ * @return array All the rules + extended.
879
+ */
880
+ public function getMobileDetectionRulesExtended()
881
+ {
882
+ static $rules;
883
+
884
+ if (!$rules) {
885
+ // Merge all rules together.
886
+ $rules = array_merge(
887
+ self::$phoneDevices,
888
+ self::$tabletDevices,
889
+ self::$operatingSystems,
890
+ self::$browsers,
891
+ self::$utilities
892
+ );
893
+ }
894
+
895
+ return $rules;
896
+ }
897
+
898
+ /**
899
+ * Retrieve the current set of rules.
900
+ *
901
+ * @deprecated since version 2.6.9
902
+ *
903
+ * @return array
904
+ */
905
+ public function getRules()
906
+ {
907
+ if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) {
908
+ return self::getMobileDetectionRulesExtended();
909
+ } else {
910
+ return self::getMobileDetectionRules();
911
+ }
912
+ }
913
+
914
+ /**
915
+ * Retrieve the list of mobile operating systems.
916
+ *
917
+ * @return array The list of mobile operating systems.
918
+ */
919
+ public static function getOperatingSystems()
920
+ {
921
+ return self::$operatingSystems;
922
+ }
923
+
924
+ /**
925
+ * Check the HTTP headers for signs of mobile.
926
+ * This is the fastest mobile check possible; it's used
927
+ * inside isMobile() method.
928
+ *
929
+ * @return bool
930
+ */
931
+ public function checkHttpHeadersForMobile()
932
+ {
933
+
934
+ foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
935
+ if ( isset($this->httpHeaders[$mobileHeader]) ) {
936
+ if ( is_array($matchType['matches']) ) {
937
+ foreach ($matchType['matches'] as $_match) {
938
+ if ( strpos($this->httpHeaders[$mobileHeader], $_match) !== false ) {
939
+ return true;
940
+ }
941
+ }
942
+
943
+ return false;
944
+ } else {
945
+ return true;
946
+ }
947
+ }
948
+ }
949
+
950
+ return false;
951
+
952
+ }
953
+
954
+ /**
955
+ * Magic overloading method.
956
+ *
957
+ * @method boolean is[...]()
958
+ * @param string $name
959
+ * @param array $arguments
960
+ * @return mixed
961
+ * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
962
+ */
963
+ public function __call($name, $arguments)
964
+ {
965
+ //make sure the name starts with 'is', otherwise
966
+ if (substr($name, 0, 2) != 'is') {
967
+ throw new BadMethodCallException("No such method exists: $name");
968
+ }
969
+
970
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
971
+
972
+ $key = substr($name, 2);
973
+
974
+ return $this->matchUAAgainstKey($key);
975
+ }
976
+
977
+ /**
978
+ * Find a detection rule that matches the current User-agent.
979
+ *
980
+ * @param null $userAgent deprecated
981
+ * @return boolean
982
+ */
983
+ protected function matchDetectionRulesAgainstUA($userAgent = null)
984
+ {
985
+ // Begin general search.
986
+ foreach ($this->getRules() as $_regex) {
987
+ if (empty($_regex)) {
988
+ continue;
989
+ }
990
+ if ($this->match($_regex, $userAgent)) {
991
+ return true;
992
+ }
993
+ }
994
+
995
+ return false;
996
+ }
997
+
998
+ /**
999
+ * Search for a certain key in the rules array.
1000
+ * If the key is found the try to match the corresponding
1001
+ * regex agains the User-Agent.
1002
+ *
1003
+ * @param string $key
1004
+ * @param null $userAgent deprecated
1005
+ * @return mixed
1006
+ */
1007
+ protected function matchUAAgainstKey($key, $userAgent = null)
1008
+ {
1009
+ // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
1010
+ $key = strtolower($key);
1011
+
1012
+ //change the keys to lower case
1013
+ $_rules = array_change_key_case($this->getRules());
1014
+
1015
+ if (array_key_exists($key, $_rules)) {
1016
+ if (empty($_rules[$key])) {
1017
+ return null;
1018
+ }
1019
+
1020
+ return $this->match($_rules[$key], $userAgent);
1021
+ }
1022
+
1023
+ return false;
1024
+ }
1025
+
1026
+ /**
1027
+ * Check if the device is mobile.
1028
+ * Returns true if any type of mobile device detected, including special ones
1029
+ * @param null $userAgent deprecated
1030
+ * @param null $httpHeaders deprecated
1031
+ * @return bool
1032
+ */
1033
+ public function isMobile($userAgent = null, $httpHeaders = null)
1034
+ {
1035
+
1036
+ if ($httpHeaders) {
1037
+ $this->setHttpHeaders($httpHeaders);
1038
+ }
1039
+
1040
+ if ($userAgent) {
1041
+ $this->setUserAgent($userAgent);
1042
+ }
1043
+
1044
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
1045
+
1046
+ if ($this->checkHttpHeadersForMobile()) {
1047
+ return true;
1048
+ } else {
1049
+ return $this->matchDetectionRulesAgainstUA();
1050
+ }
1051
+
1052
+ }
1053
+
1054
+ /**
1055
+ * Check if the device is a tablet.
1056
+ * Return true if any type of tablet device is detected.
1057
+ *
1058
+ * @param string $userAgent deprecated
1059
+ * @param array $httpHeaders deprecated
1060
+ * @return bool
1061
+ */
1062
+ public function isTablet($userAgent = null, $httpHeaders = null)
1063
+ {
1064
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
1065
+
1066
+ foreach (self::$tabletDevices as $_regex) {
1067
+ if ($this->match($_regex, $userAgent)) {
1068
+ return true;
1069
+ }
1070
+ }
1071
+
1072
+ return false;
1073
+ }
1074
+
1075
+ /**
1076
+ * This method checks for a certain property in the
1077
+ * userAgent.
1078
+ * @todo: The httpHeaders part is not yet used.
1079
+ *
1080
+ * @param string $key
1081
+ * @param string $userAgent deprecated
1082
+ * @param string $httpHeaders deprecated
1083
+ * @return bool|int|null
1084
+ */
1085
+ public function is($key, $userAgent = null, $httpHeaders = null)
1086
+ {
1087
+ // Set the UA and HTTP headers only if needed (eg. batch mode).
1088
+ if ($httpHeaders) {
1089
+ $this->setHttpHeaders($httpHeaders);
1090
+ }
1091
+
1092
+ if ($userAgent) {
1093
+ $this->setUserAgent($userAgent);
1094
+ }
1095
+
1096
+ $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
1097
+
1098
+ return $this->matchUAAgainstKey($key);
1099
+ }
1100
+
1101
+ /**
1102
+ * Some detection rules are relative (not standard),
1103
+ * because of the diversity of devices, vendors and
1104
+ * their conventions in representing the User-Agent or
1105
+ * the HTTP headers.
1106
+ *
1107
+ * This method will be used to check custom regexes against
1108
+ * the User-Agent string.
1109
+ *
1110
+ * @param $regex
1111
+ * @param string $userAgent
1112
+ * @return bool
1113
+ *
1114
+ * @todo: search in the HTTP headers too.
1115
+ */
1116
+ public function match($regex, $userAgent = null)
1117
+ {
1118
+ // Escape the special character which is the delimiter.
1119
+ $regex = str_replace('/', '\/', $regex);
1120
+ $match = (bool) preg_match('/'.$regex.'/is', (!empty($userAgent) ? $userAgent : $this->userAgent), $matches);
1121
+ // If positive match is found, store the results for debug.
1122
+ if ($match) {
1123
+ $this->matchingRegex = $regex;
1124
+ $this->matchesArray = $matches;
1125
+ }
1126
+
1127
+ return $match;
1128
+ }
1129
+
1130
+ /**
1131
+ * Get the properties array.
1132
+ *
1133
+ * @return array
1134
+ */
1135
+ public static function getProperties()
1136
+ {
1137
+ return self::$properties;
1138
+ }
1139
+
1140
+ /**
1141
+ * Prepare the version number.
1142
+ *
1143
+ * @todo Remove the error supression from str_replace() call.
1144
+ *
1145
+ * @param string $ver The string version, like "2.6.21.2152";
1146
+ *
1147
+ * @return float
1148
+ */
1149
+ public function prepareVersionNo($ver)
1150
+ {
1151
+ $ver = str_replace(array('_', ' ', '/'), '.', $ver);
1152
+ $arrVer = explode('.', $ver, 2);
1153
+
1154
+ if (isset($arrVer[1])) {
1155
+ $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
1156
+ }
1157
+
1158
+ return (float) implode('.', $arrVer);
1159
+ }
1160
+
1161
+ /**
1162
+ * Check the version of the given property in the User-Agent.
1163
+ * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
1164
+ *
1165
+ * @param string $propertyName The name of the property. See self::getProperties() array
1166
+ * keys for all possible properties.
1167
+ * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
1168
+ * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
1169
+ * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
1170
+ * invalid parameter will default to the this type as well.
1171
+ *
1172
+ * @return string|float The version of the property we are trying to extract.
1173
+ */
1174
+ public function version($propertyName, $type = self::VERSION_TYPE_STRING)
1175
+ {
1176
+ if (empty($propertyName)) {
1177
+ return false;
1178
+ }
1179
+
1180
+ //set the $type to the default if we don't recognize the type
1181
+ if ($type != self::VERSION_TYPE_STRING && $type != self::VERSION_TYPE_FLOAT) {
1182
+ $type = self::VERSION_TYPE_STRING;
1183
+ }
1184
+
1185
+ $properties = self::getProperties();
1186
+
1187
+ // Check if the property exists in the properties array.
1188
+ if (array_key_exists($propertyName, $properties)) {
1189
+
1190
+ // Prepare the pattern to be matched.
1191
+ // Make sure we always deal with an array (string is converted).
1192
+ $properties[$propertyName] = (array) $properties[$propertyName];
1193
+
1194
+ foreach ($properties[$propertyName] as $propertyMatchString) {
1195
+
1196
+ $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
1197
+
1198
+ // Escape the special character which is the delimiter.
1199
+ $propertyPattern = str_replace('/', '\/', $propertyPattern);
1200
+
1201
+ // Identify and extract the version.
1202
+ preg_match('/'.$propertyPattern.'/is', $this->userAgent, $match);
1203
+
1204
+ if (!empty($match[1])) {
1205
+ $version = ( $type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1] );
1206
+
1207
+ return $version;
1208
+ }
1209
+
1210
+ }
1211
+
1212
+ }
1213
+
1214
+ return false;
1215
+ }
1216
+
1217
+ /**
1218
+ * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
1219
+ *
1220
+ * @return string One of the self::MOBILE_GRADE_* constants.
1221
+ */
1222
+ public function mobileGrade()
1223
+ {
1224
+ $isMobile = $this->isMobile();
1225
+
1226
+ if (
1227
+ // 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)
1228
+ $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)>=4.3 ||
1229
+ $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)>=4.3 ||
1230
+ $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)>=4.3 ||
1231
+
1232
+ // 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)
1233
+ // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
1234
+ // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
1235
+ // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
1236
+ ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
1237
+
1238
+ // 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)
1239
+ $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT)>=7.5 ||
1240
+
1241
+ // Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10)
1242
+ $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=6.0 ||
1243
+ // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
1244
+ $this->match('Playbook.*Tablet') ||
1245
+
1246
+ // Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0)
1247
+ ( $this->version('webOS', self::VERSION_TYPE_FLOAT)>=1.4 && $this->match('Palm|Pre|Pixi') ) ||
1248
+ // Palm WebOS 3.0 - Tested on HP TouchPad
1249
+ $this->match('hp.*TouchPad') ||
1250
+
1251
+ // Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices
1252
+ ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=18 ) ||
1253
+
1254
+ // Chrome for Android - Tested on Android 4.0, 4.1 device
1255
+ ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=4.0 ) ||
1256
+
1257
+ // Skyfire 4.1 - Tested on Android 2.3 device
1258
+ ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT)>=4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) ||
1259
+
1260
+ // Opera Mobile 11.5-12: Tested on Android 2.3
1261
+ ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>=11.5 && $this->is('AndroidOS') ) ||
1262
+
1263
+ // Meego 1.2 - Tested on Nokia 950 and N9
1264
+ $this->is('MeeGoOS') ||
1265
+
1266
+ // Tizen (pre-release) - Tested on early hardware
1267
+ $this->is('Tizen') ||
1268
+
1269
+ // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
1270
+ // @todo: more tests here!
1271
+ $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT)>=2.0 ||
1272
+
1273
+ // UC Browser - Tested on Android 2.3 device
1274
+ ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) ||
1275
+
1276
+ // Kindle 3 and Fire - Tested on the built-in WebKit browser for each
1277
+ ( $this->match('Kindle Fire') ||
1278
+ $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT)>=3.0 ) ||
1279
+
1280
+ // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
1281
+ $this->is('AndroidOS') && $this->is('NookTablet') ||
1282
+
1283
+ // Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7
1284
+ $this->version('Chrome', self::VERSION_TYPE_FLOAT)>=16 && !$isMobile ||
1285
+
1286
+ // Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7
1287
+ $this->version('Safari', self::VERSION_TYPE_FLOAT)>=5.0 && !$isMobile ||
1288
+
1289
+ // Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7
1290
+ $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=10.0 && !$isMobile ||
1291
+
1292
+ // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
1293
+ $this->version('IE', self::VERSION_TYPE_FLOAT)>=7.0 && !$isMobile ||
1294
+
1295
+ // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
1296
+ $this->version('Opera', self::VERSION_TYPE_FLOAT)>=10 && !$isMobile
1297
+ ){
1298
+ return self::MOBILE_GRADE_A;
1299
+ }
1300
+
1301
+ if (
1302
+ $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
1303
+ $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 ||
1304
+ $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 ||
1305
+
1306
+ // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
1307
+ $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
1308
+
1309
+ //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
1310
+ ($this->version('Opera Mini', self::VERSION_TYPE_FLOAT)>=5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT)<=7.0 &&
1311
+ ($this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 || $this->is('iOS')) ) ||
1312
+
1313
+ // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
1314
+ $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
1315
+
1316
+ // @todo: report this (tested on Nokia N71)
1317
+ $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>=11 && $this->is('SymbianOS')
1318
+ ){
1319
+ return self::MOBILE_GRADE_B;
1320
+ }
1321
+
1322
+ if (
1323
+ // Blackberry 4.x - Tested on the Curve 8330
1324
+ $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<=5.0 ||
1325
+ // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
1326
+ $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT)<=5.2 ||
1327
+
1328
+ // Tested on original iPhone (3.1), iPhone 3 (3.2)
1329
+ $this->isIOS() && $this->version('iPad', self::VERSION_TYPE_FLOAT)<=3.2 ||
1330
+ $this->isIOS() && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<=3.2 ||
1331
+ $this->isIOS() && $this->version('iPod', self::VERSION_TYPE_FLOAT)<=3.2 ||
1332
+
1333
+ // Internet Explorer 7 and older - Tested on Windows XP
1334
+ $this->version('IE', self::VERSION_TYPE_FLOAT)<=7.0 && !$isMobile
1335
+ ){
1336
+ return self::MOBILE_GRADE_C;
1337
+ }
1338
+
1339
+ //All older smartphone platforms and featurephones - Any device that doesn't support media queries
1340
+ //will receive the basic, C grade experience.
1341
+ return self::MOBILE_GRADE_C;
1342
+ }
1343
+ }
1344
+ }
classes/mobile-detect.php DELETED
@@ -1,1183 +0,0 @@
1
- <?php
2
- /**
3
- * Mobile Detect Library
4
- * =====================
5
- *
6
- * Motto: "Every business should have a mobile detection script to detect mobile readers"
7
- *
8
- * Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
9
- * It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
10
- *
11
- * @author Current authors: Serban Ghita <serbanghita@gmail.com>, Nick Ilyin <nick.ilyin@gmail.com>
12
- * Original author: Victor Stanciu <vic.stanciu@gmail.com>
13
- *
14
- * @license Code and contributions have 'MIT License'
15
- * More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
16
- *
17
- * @link Homepage: http://mobiledetect.net
18
- * GitHub Repo: https://github.com/serbanghita/Mobile-Detect
19
- * Google Code: http://code.google.com/p/php-mobile-detect/
20
- * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md
21
- * HOWTO: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
22
- *
23
- * @version 2.7.6
24
- */
25
-
26
- class Mobile_Detect
27
- {
28
- /**
29
- * Mobile detection type.
30
- *
31
- * @deprecated since version 2.6.9
32
- */
33
- const DETECTION_TYPE_MOBILE = 'mobile';
34
-
35
- /**
36
- * Extended detection type.
37
- *
38
- * @deprecated since version 2.6.9
39
- */
40
- const DETECTION_TYPE_EXTENDED = 'extended';
41
-
42
- /**
43
- * A frequently used regular expression to extract version #s.
44
- *
45
- * @deprecated since version 2.6.9
46
- */
47
- const VER = '([\w._\+]+)';
48
-
49
- /**
50
- * Top-level device.
51
- */
52
- const MOBILE_GRADE_A = 'A';
53
-
54
- /**
55
- * Mid-level device.
56
- */
57
- const MOBILE_GRADE_B = 'B';
58
-
59
- /**
60
- * Low-level device.
61
- */
62
- const MOBILE_GRADE_C = 'C';
63
-
64
- /**
65
- * Stores the version number of the current release.
66
- */
67
- const VERSION = '2.7.6';
68
-
69
- /**
70
- * A type for the version() method indicating a string return value.
71
- */
72
- const VERSION_TYPE_STRING = 'text';
73
-
74
- /**
75
- * A type for the version() method indicating a float return value.
76
- */
77
- const VERSION_TYPE_FLOAT = 'float';
78
-
79
- /**
80
- * The User-Agent HTTP header is stored in here.
81
- * @var string
82
- */
83
- protected $userAgent = null;
84
-
85
- /**
86
- * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
87
- * @var array
88
- */
89
- protected $httpHeaders = array();
90
-
91
- /**
92
- * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED.
93
- *
94
- * @deprecated since version 2.6.9
95
- *
96
- * @var string
97
- */
98
- protected $detectionType = self::DETECTION_TYPE_MOBILE;
99
-
100
- /**
101
- * HTTP headers that trigger the 'isMobile' detection
102
- * to be true.
103
- *
104
- * @var array
105
- */
106
- protected static $mobileHeaders = array(
107
-
108
- 'HTTP_ACCEPT' => array('matches' => array(
109
- // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
110
- 'application/x-obml2d',
111
- // BlackBerry devices.
112
- 'application/vnd.rim.html',
113
- 'text/vnd.wap.wml',
114
- 'application/vnd.wap.xhtml+xml'
115
- )),
116
- 'HTTP_X_WAP_PROFILE' => null,
117
- 'HTTP_X_WAP_CLIENTID' => null,
118
- 'HTTP_WAP_CONNECTION' => null,
119
- 'HTTP_PROFILE' => null,
120
- // Reported by Opera on Nokia devices (eg. C3).
121
- 'HTTP_X_OPERAMINI_PHONE_UA' => null,
122
- 'HTTP_X_NOKIA_IPADDRESS' => null,
123
- 'HTTP_X_NOKIA_GATEWAY_ID' => null,
124
- 'HTTP_X_ORANGE_ID' => null,
125
- 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
126
- 'HTTP_X_HUAWEI_USERID' => null,
127
- // Reported by Windows Smartphones.
128
- 'HTTP_UA_OS' => null,
129
- // Reported by Verizon, Vodafone proxy system.
130
- 'HTTP_X_MOBILE_GATEWAY' => null,
131
- // Seend this on HTC Sensation. @ref: SensationXE_Beats_Z715e.
132
- 'HTTP_X_ATT_DEVICEID' => null,
133
- // Seen this on a HTC.
134
- 'HTTP_UA_CPU' => array('matches' => array('ARM')),
135
- );
136
-
137
- /**
138
- * List of mobile devices (phones).
139
- *
140
- * @var array
141
- */
142
- protected static $phoneDevices = array(
143
- 'iPhone' => '\biPhone.*Mobile|\biPod', // |\biTunes
144
- 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+',
145
- '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',
146
- 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile',
147
- // @todo: Is 'Dell Streak' a tablet or a phone? ;)
148
- 'Dell' => 'Dell.*Streak|Dell.*Aero|Dell.*Venue|DELL.*Venue Pro|Dell Flash|Dell Smoke|Dell Mini 3iX|XCD28|XCD35|\b001DL\b|\b101DL\b|\bGS01\b',
149
- 'Motorola' => 'Motorola|\bDroid\b.*Build|DROIDX|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',
150
- 'Samsung' => 'Samsung|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',
151
- 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|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)',
152
- 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i',
153
- 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile',
154
- // @ref: http://www.micromaxinfo.com/mobiles/smartphones
155
- // Added because the codes might conflict with Acer Tablets.
156
- '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',
157
- 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ; @todo - complete the regex.
158
- 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
159
- // @ref: http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
160
- // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
161
- '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',
162
- // @ref: http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
163
- 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
164
- // Added simvalley mobile just for fun. They have some interesting devices.
165
- // @ref: http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
166
- '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',
167
- // @Tapatalk is a mobile app; @ref: http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
168
- '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'
169
- );
170
-
171
- /**
172
- * List of tablet devices.
173
- *
174
- * @var array
175
- */
176
- protected static $tabletDevices = array(
177
- 'iPad' => 'iPad|iPad.*Mobile', // @todo: check for mobile friendly emails topic.
178
- 'NexusTablet' => '^.*Android.*Nexus(((?:(?!Mobile))|(?:(\s(7|10).+))).)*$',
179
- '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-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-I9205|GT-P5200|GT-P5210|SM-T311|SM-T310|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500',
180
- // @reference: http://www.labnol.org/software/kindle-user-agent-string/20378/
181
- 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE)\b',
182
- // Only the Surface tablets with Windows RT are considered mobile.
183
- // @ref: http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
184
- 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;',
185
- // @ref: http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
186
- 'HPTablet' => 'HP Slate 7|HP ElitePad 900|hp-tablet|EliteBook.*Touch',
187
- // @note: watch out for PadFone, see #132
188
- 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101',
189
- 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
190
- 'HTCtablet' => 'HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200',
191
- 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
192
- 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
193
- // @ref: http://www.acer.ro/ac/ro/RO/content/drivers
194
- // @ref: http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
195
- // @ref: http://us.acer.com/ac/en/US/content/group/tablets
196
- // @note: Can conflict with Micromax and Motorola phones codes.
197
- '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)\b|W3-810',
198
- // @ref: http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
199
- // @ref: http://us.toshiba.com/tablets/tablet-finder
200
- // @ref: http://www.toshiba.co.jp/regza/tablet/
201
- 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
202
- // @ref: http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
203
- 'LGTablet' => '\bL-06C|LG-V900|LG-V909\b',
204
- 'FujitsuTablet' => 'Android.*\b(F-01D|F-05E|F-10D|M532|Q572)\b',
205
- // Prestigio Tablets http://www.prestigio.com/support
206
- 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD',
207
- // @ref: http://support.lenovo.com/en_GB/downloads/default.page?#
208
- 'LenovoTablet' => 'IdeaTab|S2110|S6000|K3011|A3000|A1000|A2107|A2109|A1107|ThinkPad([ ]+)?Tablet',
209
- 'YarvikTablet' => 'Android.*(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468)',
210
- 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
211
- 'ArnovaTablet' => 'AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT',
212
- // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
213
- 'IRUTablet' => 'M702pro',
214
- 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
215
- // @ref: http://www.e-boda.ro/tablete-pc.html
216
- 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
217
- // @ref: http://www.allview.ro/produse/droseries/lista-tablete-pc/
218
- 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
219
- // @reference: http://wiki.archosfans.com/index.php?title=Main_Page
220
- 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R',
221
- // @ref: http://www.ainol.com/plugin.php?identifier=ainol&module=product
222
- 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
223
- // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
224
- // @ref: Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
225
- // @ref: http://www.sony.jp/support/tablet/
226
- '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',
227
- // @ref: db + http://www.cube-tablet.com/buy-products.html
228
- 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
229
- // @ref: http://www.cobyusa.com/?p=pcat&pcat_id=3001
230
- 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
231
- // @ref: http://www.match.net.cn/products.asp
232
- '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',
233
- // @ref: http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
234
- // @ref: http://www.imp3.net/14/show.php?itemid=20454
235
- 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
236
- // @ref: http://www.rock-chips.com/index.php?do=prod&pid=2
237
- 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
238
- // @ref: http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
239
- 'FlyTablet' => 'IQ310|Fly Vision',
240
- // @ref: http://www.bqreaders.com/gb/tablets-prices-sale.html
241
- 'bqTablet' => 'bq.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant)|Maxwell.*Lite|Maxwell.*Plus',
242
- // @ref: http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
243
- // @ref: http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
244
- 'HuaweiTablet' => 'MediaPad|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim',
245
- // Nec or Medias Tab
246
- 'NecTablet' => '\bN-06D|\bN-08D',
247
- // Pantech Tablets: http://www.pantechusa.com/phones/
248
- 'PantechTablet' => 'Pantech.*P4100',
249
- // Broncho Tablets: http://www.broncho.cn/ (hard to find)
250
- 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
251
- // @ref: http://versusuk.com/support.html
252
- 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
253
- // @ref: http://www.zync.in/index.php/our-products/tablet-phablets
254
- 'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900',
255
- // @ref: http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
256
- 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
257
- // @ref: https://www.nabitablet.com/
258
- 'NabiTablet' => 'Android.*\bNabi',
259
- 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
260
- // French Danew Tablets http://www.danew.com/produits-tablette.php
261
- 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
262
- // Texet Tablets and Readers http://www.texet.ru/tablet/
263
- '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',
264
- // @note: Avoid detecting 'PLAYSTATION 3' as mobile.
265
- 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
266
- // @ref: http://www.galapad.net/product.html
267
- 'GalapadTablet' => 'Android.*\bG1\b',
268
- // @ref: http://www.micromaxinfo.com/tablet/funbook
269
- 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
270
- // http://www.karbonnmobiles.com/products_tablet.php
271
- 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
272
- // @ref: http://www.myallfine.com/Products.asp
273
- 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
274
- // @ref: http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
275
- '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',
276
- // @ref: http://www.yonesnav.com/products/products.php
277
- 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
278
- // @ref: http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
279
- // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
280
- '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',
281
- // @ref: http://www.gloryunion.cn/products.asp
282
- // @ref: http://www.allwinnertech.com/en/apply/mobile.html
283
- // @ref: http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
284
- // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
285
- 'GUTablet' => 'TX-A1301|TX-M9002|Q702', // 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
286
- // @ref: http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
287
- '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',
288
- // @ref: http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
289
- // @todo: add more tests.
290
- 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)',
291
- // @ref: http://hclmetablet.com/India/index.php
292
- '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',
293
- // @ref: http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
294
- 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
295
- // @ref: http://www.visture.com/index.asp
296
- 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
297
- // @ref: http://www.mijncresta.nl/tablet
298
- 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
299
- // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
300
- 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
301
- // Concorde tab
302
- 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
303
- // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
304
- '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',
305
- // Modecom Tablets - http://www.modecom.eu/tablets/portal/
306
- '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',
307
- // @ref: http://www.tesco.com/direct/hudl/
308
- 'Hudl' => 'Hudl HT7S3',
309
- // @ref: http://www.telstra.com.au/home-phone/thub-2/
310
- 'TelstraTablet' => 'T-Hub2',
311
- 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|ViewPad7|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|SmartTabII10|SmartTab10|M758A|ET904',
312
- );
313
-
314
- /**
315
- * List of mobile Operating Systems.
316
- *
317
- * @var array
318
- */
319
- protected static $operatingSystems = array(
320
- 'AndroidOS' => 'Android',
321
- 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
322
- 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
323
- 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
324
- // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
325
- 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;',
326
- // @reference: http://en.wikipedia.org/wiki/Windows_Phone
327
- // http://wifeng.cn/?r=blog&a=view&id=106
328
- // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
329
- 'WindowsPhoneOS' => 'Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7',
330
- 'iOS' => '\biPhone.*Mobile|\biPod|\biPad',
331
- // http://en.wikipedia.org/wiki/MeeGo
332
- // @todo: research MeeGo in UAs
333
- 'MeeGoOS' => 'MeeGo',
334
- // http://en.wikipedia.org/wiki/Maemo
335
- // @todo: research Maemo in UAs
336
- 'MaemoOS' => 'Maemo',
337
- 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
338
- 'webOS' => 'webOS|hpwOS',
339
- 'badaOS' => '\bBada\b',
340
- 'BREWOS' => 'BREW',
341
- );
342
-
343
- /**
344
- * List of mobile User Agents.
345
- *
346
- * @var array
347
- */
348
- protected static $browsers = array(
349
- // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
350
- 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?',
351
- 'Dolfin' => '\bDolfin\b',
352
- 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+',
353
- 'Skyfire' => 'Skyfire',
354
- 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
355
- 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile',
356
- 'Bolt' => 'bolt',
357
- 'TeaShark' => 'teashark',
358
- 'Blazer' => 'Blazer',
359
- // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
360
- 'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile',
361
- // @ref: http://en.wikipedia.org/wiki/Midori_(web_browser)
362
- //'Midori' => 'midori',
363
- 'Tizen' => 'Tizen',
364
- 'UCBrowser' => 'UC.*Browser|UCWEB',
365
- // @ref: https://github.com/serbanghita/Mobile-Detect/issues/7
366
- 'DiigoBrowser' => 'DiigoBrowser',
367
- // http://www.puffinbrowser.com/index.php
368
- 'Puffin' => 'Puffin',
369
- // @ref: http://mercury-browser.com/index.html
370
- 'Mercury' => '\bMercury\b',
371
- // @reference: http://en.wikipedia.org/wiki/Minimo
372
- // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
373
- 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger'
374
- );
375
-
376
- /**
377
- * Utilities.
378
- *
379
- * @var array
380
- */
381
- protected static $utilities = array(
382
- // Experimental. When a mobile device wants to switch to 'Desktop Mode'.
383
- // @ref: http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
384
- // @ref: https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
385
- 'DesktopMode' => 'WPDesktop',
386
- 'TV' => 'SonyDTV|HbbTV', // experimental
387
- 'WebKit' => '(webkit)[ /]([\w.]+)',
388
- 'Bot' => 'Googlebot|DoCoMo|YandexBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|facebookexternalhit',
389
- 'MobileBot' => 'Googlebot-Mobile|DoCoMo|YahooSeeker/M1A1-R2D2',
390
- 'Console' => '\b(Nintendo|Nintendo WiiU|PLAYSTATION|Xbox)\b',
391
- 'Watch' => 'SM-V700',
392
- );
393
-
394
- /**
395
- * All possible HTTP headers that represent the
396
- * User-Agent string.
397
- *
398
- * @var array
399
- */
400
- protected static $uaHttpHeaders = array(
401
- // The default User-Agent string.
402
- 'HTTP_USER_AGENT',
403
- // Header can occur on devices using Opera Mini.
404
- 'HTTP_X_OPERAMINI_PHONE_UA',
405
- // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
406
- 'HTTP_X_DEVICE_USER_AGENT',
407
- 'HTTP_X_ORIGINAL_USER_AGENT',
408
- 'HTTP_X_SKYFIRE_PHONE',
409
- 'HTTP_X_BOLT_PHONE_UA',
410
- 'HTTP_DEVICE_STOCK_UA',
411
- 'HTTP_X_UCBROWSER_DEVICE_UA'
412
- );
413
-
414
- /**
415
- * The individual segments that could exist in a User-Agent string. VER refers to the regular
416
- * expression defined in the constant self::VER.
417
- *
418
- * @var array
419
- */
420
- protected static $properties = array(
421
-
422
- // Build
423
- 'Mobile' => 'Mobile/[VER]',
424
- 'Build' => 'Build/[VER]',
425
- 'Version' => 'Version/[VER]',
426
- 'VendorID' => 'VendorID/[VER]',
427
-
428
- // Devices
429
- 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
430
- 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
431
- 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
432
- //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
433
- 'Kindle' => 'Kindle/[VER]',
434
-
435
- // Browser
436
- 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
437
- 'Coast' => array('Coast/[VER]'),
438
- 'Dolfin' => 'Dolfin/[VER]',
439
- // @reference: https://developer.mozilla.org/en-US/docs/User_Agent_Strings_Reference
440
- 'Firefox' => 'Firefox/[VER]',
441
- 'Fennec' => 'Fennec/[VER]',
442
- // @reference: http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
443
- 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];'),
444
- // http://en.wikipedia.org/wiki/NetFront
445
- 'NetFront' => 'NetFront/[VER]',
446
- 'NokiaBrowser' => 'NokiaBrowser/[VER]',
447
- 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
448
- 'Opera Mini' => 'Opera Mini/[VER]',
449
- 'Opera Mobi' => 'Version/[VER]',
450
- 'UC Browser' => 'UC Browser[VER]',
451
- 'MQQBrowser' => 'MQQBrowser/[VER]',
452
- 'MicroMessenger' => 'MicroMessenger/[VER]',
453
- // @note: Safari 7534.48.3 is actually Version 5.1.
454
- // @note: On BlackBerry the Version is overwriten by the OS.
455
- 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
456
- 'Skyfire' => 'Skyfire/[VER]',
457
- 'Tizen' => 'Tizen/[VER]',
458
- 'Webkit' => 'webkit[ /][VER]',
459
-
460
- // Engine
461
- 'Gecko' => 'Gecko/[VER]',
462
- 'Trident' => 'Trident/[VER]',
463
- 'Presto' => 'Presto/[VER]',
464
-
465
- // OS
466
- 'iOS' => ' \bOS\b [VER] ',
467
- 'Android' => 'Android [VER]',
468
- 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
469
- 'BREW' => 'BREW [VER]',
470
- 'Java' => 'Java/[VER]',
471
- // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
472
- // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
473
- 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'),
474
- 'Windows Phone' => 'Windows Phone [VER]',
475
- 'Windows CE' => 'Windows CE/[VER]',
476
- // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
477
- 'Windows NT' => 'Windows NT [VER]',
478
- 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
479
- 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'),
480
- );
481
-
482
- /**
483
- * Construct an instance of this class.
484
- *
485
- * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored.
486
- * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
487
- * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
488
- * from the $headers array instead.
489
- */
490
- public function __construct(
491
- array $headers = null,
492
- $userAgent = null
493
- ){
494
- $this->setHttpHeaders($headers);
495
- $this->setUserAgent($userAgent);
496
- }
497
-
498
- /**
499
- * Get the current script version.
500
- * This is useful for the demo.php file,
501
- * so people can check on what version they are testing
502
- * for mobile devices.
503
- *
504
- * @return string The version number in semantic version format.
505
- */
506
- public static function getScriptVersion()
507
- {
508
- return self::VERSION;
509
- }
510
-
511
- /**
512
- * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
513
- *
514
- * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
515
- * the headers. The default null is left for backwards compatibilty.
516
- */
517
- public function setHttpHeaders($httpHeaders = null)
518
- {
519
- //use global _SERVER if $httpHeaders aren't defined
520
- if (!is_array($httpHeaders) || !count($httpHeaders)) {
521
- $httpHeaders = $_SERVER;
522
- }
523
-
524
- //clear existing headers
525
- $this->httpHeaders = array();
526
-
527
- //Only save HTTP headers. In PHP land, that means only _SERVER vars that
528
- //start with HTTP_.
529
- foreach ($httpHeaders as $key => $value) {
530
- if (substr($key,0,5) == 'HTTP_') {
531
- $this->httpHeaders[$key] = $value;
532
- }
533
- }
534
- }
535
-
536
- /**
537
- * Retrieves the HTTP headers.
538
- *
539
- * @return array
540
- */
541
- public function getHttpHeaders()
542
- {
543
- return $this->httpHeaders;
544
- }
545
-
546
- /**
547
- * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
548
- * Simply null is returned.
549
- *
550
- * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
551
- * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
552
- * all-caps, HTTP_ prefixed, underscore seperated awesomeness.
553
- *
554
- * @return string|null The value of the header.
555
- */
556
- public function getHttpHeader($header)
557
- {
558
- //are we using PHP-flavored headers?
559
- if (strpos($header, '_') === false) {
560
- $header = str_replace('-', '_', $header);
561
- $header = strtoupper($header);
562
- }
563
-
564
- //test the alternate, too
565
- $altHeader = 'HTTP_' . $header;
566
-
567
- //Test both the regular and the HTTP_ prefix
568
- if (isset($this->httpHeaders[$header])) {
569
- return $this->httpHeaders[$header];
570
- } elseif (isset($this->httpHeaders[$altHeader])) {
571
- return $this->httpHeaders[$altHeader];
572
- }
573
- }
574
-
575
- public function getMobileHeaders()
576
- {
577
- return self::$mobileHeaders;
578
- }
579
-
580
- /**
581
- * Get all possible HTTP headers that
582
- * can contain the User-Agent string.
583
- *
584
- * @return array List of HTTP headers.
585
- */
586
- public function getUaHttpHeaders()
587
- {
588
- return self::$uaHttpHeaders;
589
- }
590
-
591
- /**
592
- * Set the User-Agent to be used.
593
- *
594
- * @param string $userAgent The user agent string to set.
595
- */
596
- public function setUserAgent($userAgent = null)
597
- {
598
- if (!empty($userAgent)) {
599
- return $this->userAgent = $userAgent;
600
- } else {
601
-
602
- $this->userAgent = null;
603
-
604
- foreach($this->getUaHttpHeaders() as $altHeader){
605
- if(!empty($this->httpHeaders[$altHeader])){ // @todo: should use getHttpHeader(), but it would be slow. (Serban)
606
- $this->userAgent .= $this->httpHeaders[$altHeader] . " ";
607
- }
608
- }
609
-
610
- return $this->userAgent = (!empty($this->userAgent) ? trim($this->userAgent) : null);
611
-
612
- }
613
- }
614
-
615
- /**
616
- * Retrieve the User-Agent.
617
- *
618
- * @return string|null The user agent if it's set.
619
- */
620
- public function getUserAgent()
621
- {
622
- return $this->userAgent;
623
- }
624
-
625
- /**
626
- * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or
627
- * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set.
628
- *
629
- * @deprecated since version 2.6.9
630
- *
631
- * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default
632
- * parameter is null which will default to self::DETECTION_TYPE_MOBILE.
633
- */
634
- public function setDetectionType($type = null)
635
- {
636
- if ($type === null) {
637
- $type = self::DETECTION_TYPE_MOBILE;
638
- }
639
-
640
- if ($type != self::DETECTION_TYPE_MOBILE && $type != self::DETECTION_TYPE_EXTENDED) {
641
- return;
642
- }
643
-
644
- $this->detectionType = $type;
645
- }
646
-
647
- /**
648
- * Retrieve the list of known phone devices.
649
- *
650
- * @return array List of phone devices.
651
- */
652
- public static function getPhoneDevices()
653
- {
654
- return self::$phoneDevices;
655
- }
656
-
657
- /**
658
- * Retrieve the list of known tablet devices.
659
- *
660
- * @return array List of tablet devices.
661
- */
662
- public static function getTabletDevices()
663
- {
664
- return self::$tabletDevices;
665
- }
666
-
667
- /**
668
- * Alias for getBrowsers() method.
669
- *
670
- * @return array List of user agents.
671
- */
672
- public static function getUserAgents()
673
- {
674
- return self::getBrowsers();
675
- }
676
-
677
- /**
678
- * Retrieve the list of known browsers. Specifically, the user agents.
679
- *
680
- * @return array List of browsers / user agents.
681
- */
682
- public static function getBrowsers()
683
- {
684
- return self::$browsers;
685
- }
686
-
687
- /**
688
- * Retrieve the list of known utilities.
689
- *
690
- * @return array List of utilities.
691
- */
692
- public static function getUtilities()
693
- {
694
- return self::$utilities;
695
- }
696
-
697
- /**
698
- * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
699
- *
700
- * @deprecated since version 2.6.9
701
- *
702
- * @return array All the rules (but not extended).
703
- */
704
- public static function getMobileDetectionRules()
705
- {
706
- static $rules;
707
-
708
- if (!$rules) {
709
- $rules = array_merge(
710
- self::$phoneDevices,
711
- self::$tabletDevices,
712
- self::$operatingSystems,
713
- self::$browsers
714
- );
715
- }
716
-
717
- return $rules;
718
-
719
- }
720
-
721
- /**
722
- * Method gets the mobile detection rules + utilities.
723
- * The reason this is separate is because utilities rules
724
- * don't necessary imply mobile. This method is used inside
725
- * the new $detect->is('stuff') method.
726
- *
727
- * @deprecated since version 2.6.9
728
- *
729
- * @return array All the rules + extended.
730
- */
731
- public function getMobileDetectionRulesExtended()
732
- {
733
- static $rules;
734
-
735
- if (!$rules) {
736
- // Merge all rules together.
737
- $rules = array_merge(
738
- self::$phoneDevices,
739
- self::$tabletDevices,
740
- self::$operatingSystems,
741
- self::$browsers,
742
- self::$utilities
743
- );
744
- }
745
-
746
- return $rules;
747
- }
748
-
749
- /**
750
- * Retrieve the current set of rules.
751
- *
752
- * @deprecated since version 2.6.9
753
- *
754
- * @return array
755
- */
756
- public function getRules()
757
- {
758
- if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) {
759
- return self::getMobileDetectionRulesExtended();
760
- } else {
761
- return self::getMobileDetectionRules();
762
- }
763
- }
764
-
765
- /**
766
- * Retrieve the list of mobile operating systems.
767
- *
768
- * @return array The list of mobile operating systems.
769
- */
770
- public static function getOperatingSystems()
771
- {
772
- return self::$operatingSystems;
773
- }
774
-
775
- /**
776
- * Check the HTTP headers for signs of mobile.
777
- * This is the fastest mobile check possible; it's used
778
- * inside isMobile() method.
779
- *
780
- * @return bool
781
- */
782
- public function checkHttpHeadersForMobile()
783
- {
784
-
785
- foreach($this->getMobileHeaders() as $mobileHeader => $matchType){
786
- if( isset($this->httpHeaders[$mobileHeader]) ){
787
- if( is_array($matchType['matches']) ){
788
- foreach($matchType['matches'] as $_match){
789
- if( strpos($this->httpHeaders[$mobileHeader], $_match) !== false ){
790
- return true;
791
- }
792
- }
793
- return false;
794
- } else {
795
- return true;
796
- }
797
- }
798
- }
799
-
800
- return false;
801
-
802
- }
803
-
804
- /**
805
- * Magic overloading method.
806
- *
807
- * @method boolean is[...]()
808
- * @param string $name
809
- * @param array $arguments
810
- * @return mixed
811
- * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
812
- */
813
- public function __call($name, $arguments)
814
- {
815
- //make sure the name starts with 'is', otherwise
816
- if (substr($name, 0, 2) != 'is') {
817
- throw new BadMethodCallException("No such method exists: $name");
818
- }
819
-
820
- $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
821
-
822
- $key = substr($name, 2);
823
-
824
- return $this->matchUAAgainstKey($key);
825
- }
826
-
827
- /**
828
- * Find a detection rule that matches the current User-agent.
829
- *
830
- * @param null $userAgent deprecated
831
- * @return boolean
832
- */
833
- protected function matchDetectionRulesAgainstUA($userAgent = null)
834
- {
835
- // Begin general search.
836
- foreach ($this->getRules() as $_regex) {
837
- if (empty($_regex)) {
838
- continue;
839
- }
840
- if ($this->match($_regex, $userAgent)) {
841
- return true;
842
- }
843
- }
844
-
845
- return false;
846
- }
847
-
848
- /**
849
- * Search for a certain key in the rules array.
850
- * If the key is found the try to match the corresponding
851
- * regex agains the User-Agent.
852
- *
853
- * @param string $key
854
- * @param null $userAgent deprecated
855
- * @return mixed
856
- */
857
- protected function matchUAAgainstKey($key, $userAgent = null)
858
- {
859
- // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
860
- $key = strtolower($key);
861
-
862
- //change the keys to lower case
863
- $_rules = array_change_key_case($this->getRules());
864
-
865
- if (array_key_exists($key, $_rules)) {
866
- if (empty($_rules[$key])) {
867
- return null;
868
- }
869
-
870
- return $this->match($_rules[$key], $userAgent);
871
- }
872
-
873
- return false;
874
- }
875
-
876
- /**
877
- * Check if the device is mobile.
878
- * Returns true if any type of mobile device detected, including special ones
879
- * @param null $userAgent deprecated
880
- * @param null $httpHeaders deprecated
881
- * @return bool
882
- */
883
- public function isMobile($userAgent = null, $httpHeaders = null)
884
- {
885
-
886
- if ($httpHeaders) {
887
- $this->setHttpHeaders($httpHeaders);
888
- }
889
-
890
- if ($userAgent) {
891
- $this->setUserAgent($userAgent);
892
- }
893
-
894
- $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
895
-
896
- if ($this->checkHttpHeadersForMobile()) {
897
- return true;
898
- } else {
899
- return $this->matchDetectionRulesAgainstUA();
900
- }
901
-
902
- }
903
-
904
- /**
905
- * Check if the device is a tablet.
906
- * Return true if any type of tablet device is detected.
907
- *
908
- * @param string $userAgent deprecated
909
- * @param array $httpHeaders deprecated
910
- * @return bool
911
- */
912
- public function isTablet($userAgent = null, $httpHeaders = null)
913
- {
914
- $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
915
-
916
- foreach (self::$tabletDevices as $_regex) {
917
- if ($this->match($_regex, $userAgent)) {
918
- return true;
919
- }
920
- }
921
-
922
- return false;
923
- }
924
-
925
- /**
926
- * This method checks for a certain property in the
927
- * userAgent.
928
- * @todo: The httpHeaders part is not yet used.
929
- *
930
- * @param $key
931
- * @param string $userAgent deprecated
932
- * @param string $httpHeaders deprecated
933
- * @return bool|int|null
934
- */
935
- public function is($key, $userAgent = null, $httpHeaders = null)
936
- {
937
- // Set the UA and HTTP headers only if needed (eg. batch mode).
938
- if ($httpHeaders) {
939
- $this->setHttpHeaders($httpHeaders);
940
- }
941
-
942
- if ($userAgent) {
943
- $this->setUserAgent($userAgent);
944
- }
945
-
946
- $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
947
-
948
- return $this->matchUAAgainstKey($key);
949
- }
950
-
951
- /**
952
- * Some detection rules are relative (not standard),
953
- * because of the diversity of devices, vendors and
954
- * their conventions in representing the User-Agent or
955
- * the HTTP headers.
956
- *
957
- * This method will be used to check custom regexes against
958
- * the User-Agent string.
959
- *
960
- * @param $regex
961
- * @param string $userAgent
962
- * @return bool
963
- *
964
- * @todo: search in the HTTP headers too.
965
- */
966
- public function match($regex, $userAgent = null)
967
- {
968
- // Escape the special character which is the delimiter.
969
- $regex = str_replace('/', '\/', $regex);
970
-
971
- return (bool) preg_match('/'.$regex.'/is', (!empty($userAgent) ? $userAgent : $this->userAgent));
972
- }
973
-
974
- /**
975
- * Get the properties array.
976
- *
977
- * @return array
978
- */
979
- public static function getProperties()
980
- {
981
- return self::$properties;
982
- }
983
-
984
- /**
985
- * Prepare the version number.
986
- *
987
- * @todo Remove the error supression from str_replace() call.
988
- *
989
- * @param string $ver The string version, like "2.6.21.2152";
990
- *
991
- * @return float
992
- */
993
- public function prepareVersionNo($ver)
994
- {
995
- $ver = str_replace(array('_', ' ', '/'), '.', $ver);
996
- $arrVer = explode('.', $ver, 2);
997
-
998
- if (isset($arrVer[1])) {
999
- $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
1000
- }
1001
-
1002
- return (float) implode('.', $arrVer);
1003
- }
1004
-
1005
- /**
1006
- * Check the version of the given property in the User-Agent.
1007
- * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
1008
- *
1009
- * @param string $propertyName The name of the property. See self::getProperties() array
1010
- * keys for all possible properties.
1011
- * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
1012
- * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
1013
- * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
1014
- * invalid parameter will default to the this type as well.
1015
- *
1016
- * @return string|float The version of the property we are trying to extract.
1017
- */
1018
- public function version($propertyName, $type = self::VERSION_TYPE_STRING)
1019
- {
1020
- if (empty($propertyName)) {
1021
- return false;
1022
- }
1023
-
1024
- //set the $type to the default if we don't recognize the type
1025
- if ($type != self::VERSION_TYPE_STRING && $type != self::VERSION_TYPE_FLOAT) {
1026
- $type = self::VERSION_TYPE_STRING;
1027
- }
1028
-
1029
- $properties = self::getProperties();
1030
-
1031
- // Check if the property exists in the properties array.
1032
- if (array_key_exists($propertyName, $properties)) {
1033
-
1034
- // Prepare the pattern to be matched.
1035
- // Make sure we always deal with an array (string is converted).
1036
- $properties[$propertyName] = (array) $properties[$propertyName];
1037
-
1038
- foreach ($properties[$propertyName] as $propertyMatchString) {
1039
-
1040
- $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
1041
-
1042
- // Escape the special character which is the delimiter.
1043
- $propertyPattern = str_replace('/', '\/', $propertyPattern);
1044
-
1045
- // Identify and extract the version.
1046
- preg_match('/'.$propertyPattern.'/is', $this->userAgent, $match);
1047
-
1048
- if (!empty($match[1])) {
1049
- $version = ( $type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1] );
1050
-
1051
- return $version;
1052
- }
1053
-
1054
- }
1055
-
1056
- }
1057
-
1058
- return false;
1059
- }
1060
-
1061
- /**
1062
- * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
1063
- *
1064
- * @return string One of the self::MOBILE_GRADE_* constants.
1065
- */
1066
- public function mobileGrade()
1067
- {
1068
- $isMobile = $this->isMobile();
1069
-
1070
- if (
1071
- // Apple iOS 3.2-5.1 - Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3), iPad 3 (5.1), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), 4 (4.3 / 5.0), and 4S (5.1)
1072
- $this->version('iPad', self::VERSION_TYPE_FLOAT)>=4.3 ||
1073
- $this->version('iPhone', self::VERSION_TYPE_FLOAT)>=3.1 ||
1074
- $this->version('iPod', self::VERSION_TYPE_FLOAT)>=3.1 ||
1075
-
1076
- // 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)
1077
- // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
1078
- // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
1079
- // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
1080
- ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
1081
-
1082
- // Windows Phone 7-7.5 - Tested on the HTC Surround (7.0) HTC Trophy (7.5), LG-E900 (7.5), Nokia Lumia 800
1083
- $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT)>=7.0 ||
1084
-
1085
- // Blackberry 7 - Tested on BlackBerry® Torch 9810
1086
- // Blackberry 6.0 - Tested on the Torch 9800 and Style 9670
1087
- $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=6.0 ||
1088
- // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
1089
- $this->match('Playbook.*Tablet') ||
1090
-
1091
- // Palm WebOS (1.4-2.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0)
1092
- ( $this->version('webOS', self::VERSION_TYPE_FLOAT)>=1.4 && $this->match('Palm|Pre|Pixi') ) ||
1093
- // Palm WebOS 3.0 - Tested on HP TouchPad
1094
- $this->match('hp.*TouchPad') ||
1095
-
1096
- // Firefox Mobile (12 Beta) - Tested on Android 2.3 device
1097
- ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=12 ) ||
1098
-
1099
- // Chrome for Android - Tested on Android 4.0, 4.1 device
1100
- ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=4.0 ) ||
1101
-
1102
- // Skyfire 4.1 - Tested on Android 2.3 device
1103
- ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT)>=4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) ||
1104
-
1105
- // Opera Mobile 11.5-12: Tested on Android 2.3
1106
- ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>11 && $this->is('AndroidOS') ) ||
1107
-
1108
- // Meego 1.2 - Tested on Nokia 950 and N9
1109
- $this->is('MeeGoOS') ||
1110
-
1111
- // Tizen (pre-release) - Tested on early hardware
1112
- $this->is('Tizen') ||
1113
-
1114
- // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
1115
- // @todo: more tests here!
1116
- $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT)>=2.0 ||
1117
-
1118
- // UC Browser - Tested on Android 2.3 device
1119
- ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 ) ||
1120
-
1121
- // Kindle 3 and Fire - Tested on the built-in WebKit browser for each
1122
- ( $this->match('Kindle Fire') ||
1123
- $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT)>=3.0 ) ||
1124
-
1125
- // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
1126
- $this->is('AndroidOS') && $this->is('NookTablet') ||
1127
-
1128
- // Chrome Desktop 11-21 - Tested on OS X 10.7 and Windows 7
1129
- $this->version('Chrome', self::VERSION_TYPE_FLOAT)>=11 && !$isMobile ||
1130
-
1131
- // Safari Desktop 4-5 - Tested on OS X 10.7 and Windows 7
1132
- $this->version('Safari', self::VERSION_TYPE_FLOAT)>=5.0 && !$isMobile ||
1133
-
1134
- // Firefox Desktop 4-13 - Tested on OS X 10.7 and Windows 7
1135
- $this->version('Firefox', self::VERSION_TYPE_FLOAT)>=4.0 && !$isMobile ||
1136
-
1137
- // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
1138
- $this->version('MSIE', self::VERSION_TYPE_FLOAT)>=7.0 && !$isMobile ||
1139
-
1140
- // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
1141
- // @reference: http://my.opera.com/community/openweb/idopera/
1142
- $this->version('Opera', self::VERSION_TYPE_FLOAT)>=10 && !$isMobile
1143
-
1144
- ){
1145
- return self::MOBILE_GRADE_A;
1146
- }
1147
-
1148
- if (
1149
- $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
1150
- $this->version('iPhone', self::VERSION_TYPE_FLOAT)<3.1 ||
1151
- $this->version('iPod', self::VERSION_TYPE_FLOAT)<3.1 ||
1152
-
1153
- // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
1154
- $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)>=5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
1155
-
1156
- //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
1157
- ( $this->version('Opera Mini', self::VERSION_TYPE_FLOAT)>=5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT)<=6.5 &&
1158
- ($this->version('Android', self::VERSION_TYPE_FLOAT)>=2.3 || $this->is('iOS')) ) ||
1159
-
1160
- // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
1161
- $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
1162
-
1163
- // @todo: report this (tested on Nokia N71)
1164
- $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT)>=11 && $this->is('SymbianOS')
1165
- ){
1166
- return self::MOBILE_GRADE_B;
1167
- }
1168
-
1169
- if (
1170
- // Blackberry 4.x - Tested on the Curve 8330
1171
- $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<5.0 ||
1172
- // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
1173
- $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT)<=5.2
1174
-
1175
- ){
1176
- return self::MOBILE_GRADE_C;
1177
- }
1178
-
1179
- //All older smartphone platforms and featurephones - Any device that doesn't support media queries
1180
- //will receive the basic, C grade experience.
1181
- return self::MOBILE_GRADE_C;
1182
- }
1183
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
icegram.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Icegram
4
  * Plugin URI: http://www.icegram.com/
5
  * Description: All in one solution to inspire, convert and engage your audiences. Action bars, Popup windows, Messengers, Toast notifications and more. Awesome themes and powerful rules.
6
- * Version: 1.8.5
7
  * Author: icegram
8
  * Author URI: http://www.icegram.com/
9
  *
@@ -34,7 +34,7 @@ class Icegram {
34
 
35
  function __construct() {
36
 
37
- $this->version = "1.8.5";
38
  $this->shortcode_instances = array();
39
  $this->mode = 'local';
40
  $this->plugin_url = untrailingslashit( plugins_url( '/', __FILE__ ) );
@@ -696,7 +696,7 @@ class Icegram {
696
  }
697
 
698
  public static function get_platform() {
699
- $mobile_detect = new Mobile_Detect();
700
  $mobile_detect->setUserAgent();
701
  if($mobile_detect->isMobile()){
702
  return ($mobile_detect->isTablet()) ? 'tablet' : 'mobile';
@@ -729,10 +729,13 @@ class Icegram {
729
  $message_data_query .= " AND post_id IN ( " . implode( ',', $message_ids ) . " )";
730
  $message_data_results = $wpdb->get_results( $message_data_query, 'ARRAY_A' );
731
  foreach ( $message_data_results as $message_data_result ) {
732
- $message_data[$message_data_result['post_id']] = maybe_unserialize( $message_data_result['meta_value'] );
733
- // For WPML compatibility
734
- if (!empty( $original_message_id_map[ $message_data_result['post_id'] ])) {
735
- $message_data[$message_data_result['post_id']]['original_message_id'] = $original_message_id_map[ $message_data_result['post_id'] ];
 
 
 
736
  }
737
  }
738
  }
@@ -788,6 +791,11 @@ class Icegram {
788
  $valid_messages = $this->get_message_data( $message_ids, $preview_mode );
789
 
790
  foreach ($valid_messages as $id => $message_data) {
 
 
 
 
 
791
  // Remove message if message type is uninstalled
792
  $class_name = 'Icegram_Message_Type_' . str_replace(' ', '_', ucwords(str_replace('-', ' ', $message_data['type'])));
793
  if( !class_exists( $class_name ) ) {
3
  * Plugin Name: Icegram
4
  * Plugin URI: http://www.icegram.com/
5
  * Description: All in one solution to inspire, convert and engage your audiences. Action bars, Popup windows, Messengers, Toast notifications and more. Awesome themes and powerful rules.
6
+ * Version: 1.8.6
7
  * Author: icegram
8
  * Author URI: http://www.icegram.com/
9
  *
34
 
35
  function __construct() {
36
 
37
+ $this->version = "1.8.6";
38
  $this->shortcode_instances = array();
39
  $this->mode = 'local';
40
  $this->plugin_url = untrailingslashit( plugins_url( '/', __FILE__ ) );
696
  }
697
 
698
  public static function get_platform() {
699
+ $mobile_detect = new Ig_Mobile_Detect();
700
  $mobile_detect->setUserAgent();
701
  if($mobile_detect->isMobile()){
702
  return ($mobile_detect->isTablet()) ? 'tablet' : 'mobile';
729
  $message_data_query .= " AND post_id IN ( " . implode( ',', $message_ids ) . " )";
730
  $message_data_results = $wpdb->get_results( $message_data_query, 'ARRAY_A' );
731
  foreach ( $message_data_results as $message_data_result ) {
732
+ $data = maybe_unserialize( $message_data_result['meta_value'] );
733
+ if (!empty($data)) {
734
+ $message_data[$message_data_result['post_id']] = $data;
735
+ // For WPML compatibility
736
+ if (!empty( $original_message_id_map[ $message_data_result['post_id'] ])) {
737
+ $message_data[$message_data_result['post_id']]['original_message_id'] = $original_message_id_map[ $message_data_result['post_id'] ];
738
+ }
739
  }
740
  }
741
  }
791
  $valid_messages = $this->get_message_data( $message_ids, $preview_mode );
792
 
793
  foreach ($valid_messages as $id => $message_data) {
794
+ // Remove message if required fields are missing
795
+ if (empty($message_data) || empty($message_data['type'])) {
796
+ unset( $valid_messages[$id] );
797
+ continue;
798
+ }
799
  // Remove message if message type is uninstalled
800
  $class_name = 'Icegram_Message_Type_' . str_replace(' ', '_', ucwords(str_replace('-', ' ', $message_data['type'])));
801
  if( !class_exists( $class_name ) ) {
message-types/action-bar/themes/bold.css CHANGED
@@ -56,7 +56,7 @@
56
  -moz-border-radius: 3px;
57
  -webkit-border-radius: 3px;
58
  border-radius: 3px;
59
- text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
60
  padding: .7em 2em;
61
  }
62
 
56
  -moz-border-radius: 3px;
57
  -webkit-border-radius: 3px;
58
  border-radius: 3px;
59
+ text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1);
60
  padding: .7em 2em;
61
  }
62
 
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://www.icegram.com/
4
  Tags: popup, window, hellobar, optin, lead capture, marketing, form, notification, messenger, targeting, monster, ninja
5
  Requires at least: 3.9
6
  Tested up to: 4.1
7
- Stable tag: 1.8.5
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -153,6 +153,9 @@ Contact Us, provide as much detail of the problem as you can. We will try to sol
153
  3. Editing an Icegram campaign - add multiple messages, edit them, targeting rules, full HTML editor, themes and more
154
 
155
  == Upgrade Notice ==
 
 
 
156
  = 1.8.5 =
157
  Bug fix release for device targeting, preview before publish not working.
158
 
@@ -203,6 +206,13 @@ Initial Release
203
 
204
 
205
  == Changelog ==
 
 
 
 
 
 
 
206
  = 1.8.5 =
207
  * New: Added a link to Form Embed Documentation for easy access
208
  * Fix: Default mode of content editor set to visual
4
  Tags: popup, window, hellobar, optin, lead capture, marketing, form, notification, messenger, targeting, monster, ninja
5
  Requires at least: 3.9
6
  Tested up to: 4.1
7
+ Stable tag: 1.8.6
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
153
  3. Editing an Icegram campaign - add multiple messages, edit them, targeting rules, full HTML editor, themes and more
154
 
155
  == Upgrade Notice ==
156
+ = 1.8.6 =
157
+ Fix for "white screen" errors, updated device detection, more readable call to action button colors
158
+
159
  = 1.8.5 =
160
  Bug fix release for device targeting, preview before publish not working.
161
 
206
 
207
 
208
  == Changelog ==
209
+ = 1.8.6 =
210
+ * Fix: "Blank / White Screen of death" / Fatal error - Cannot redeclare class Mobile_Detect
211
+ * Fix: Warning for undefined index
212
+ * Update: Mobile Detect Library updated to version 2.8.11
213
+ * Update: Improvmetns in complementary colors for call to action button and button text
214
+ * Update: Action Bar's Bold theme - reduced shadown on button text
215
+
216
  = 1.8.5 =
217
  * New: Added a link to Form Embed Documentation for easy access
218
  * Fix: Default mode of content editor set to visual