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

Version Description

Bug fix release for device targeting, preview before publish not working.

Download this release

Release Info

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

Code changes from version 1.8.4 to 1.8.5

assets/css/admin.css CHANGED
@@ -263,7 +263,7 @@ input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer
263
  }
264
 
265
  .message_image_button .image-buttons-icon{
266
- margin: 4px 2px;
267
  }
268
  .message_image_button .image-buttons-icon:before {
269
  content: "\f128";
@@ -273,7 +273,7 @@ input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer
273
  vertical-align: top !important;
274
  }
275
  .message_headline_button .headline-buttons-icon {
276
- margin: 5px 2px;
277
  }
278
 
279
  .message_headline_button .headline-buttons-icon:before {
@@ -638,18 +638,26 @@ CSS for landing page and Help and Support
638
  border: 1px solid #CCC;
639
  }
640
 
641
- #embed_form {
642
- width: 600px !important;
643
- height: 495px !important;
644
- margin: 0em !important;
 
 
645
  }
646
-
647
- #embed_form input[value="ig_vertical"]{
648
  margin-left: 3%;
649
  }
650
- #embed_form select{
651
  width:30%;
652
  }
 
 
 
 
 
 
 
653
 
654
  /** Fix to keep the message setting row indepenent of other themes **/
655
  .campaign_target_rules_panel .messages_list_table p.inline{
@@ -689,5 +697,4 @@ CSS for landing page and Help and Support
689
  font-family: dashicons;
690
  content: "\f132";
691
  vertical-align: middle;
692
- }
693
-
263
  }
264
 
265
  .message_image_button .image-buttons-icon{
266
+ margin: 0 2px;
267
  }
268
  .message_image_button .image-buttons-icon:before {
269
  content: "\f128";
273
  vertical-align: top !important;
274
  }
275
  .message_headline_button .headline-buttons-icon {
276
+ margin: 2px;
277
  }
278
 
279
  .message_headline_button .headline-buttons-icon:before {
638
  border: 1px solid #CCC;
639
  }
640
 
641
+ /** Embed Form CSS - Start**/
642
+ #popup_container {
643
+ width: 550px;
644
+ background-color: #FFF;
645
+ margin: 0 auto;
646
+ padding: .5em 1em 1em 1em;
647
  }
648
+ #popup_container input[value="ig_vertical"]{
 
649
  margin-left: 3%;
650
  }
651
+ #popup_container select{
652
  width:30%;
653
  }
654
+ #popup_container #embed_howto_link {
655
+ float: right;
656
+ font-size: .8em;
657
+ font-weight: normal;
658
+ }
659
+ /** Embed Form CSS - End**/
660
+
661
 
662
  /** Fix to keep the message setting row indepenent of other themes **/
663
  .campaign_target_rules_panel .messages_list_table p.inline{
697
  font-family: dashicons;
698
  content: "\f132";
699
  vertical-align: middle;
700
+ }
 
assets/css/frontend.css CHANGED
@@ -89,7 +89,7 @@ div.icegram .ig_embed_form.ig_vertical input{
89
  margin-right: 4%;
90
  }
91
 
92
- div.icegram .ig_embed_form.ig_horizontal li{
93
  display: inline-block;
94
  min-width: 24%;
95
  }
89
  margin-right: 4%;
90
  }
91
 
92
+ div.icegram .ig_embed_form.ig_horizontal .ig_embed_form_container li{
93
  display: inline-block;
94
  min-width: 24%;
95
  }
assets/css/magnific-popup.css CHANGED
@@ -16,7 +16,9 @@
16
  left: 0;
17
  width: 100%;
18
  height: 100%;
19
- z-index: 1043;
 
 
20
  position: fixed;
21
  outline: none !important;
22
  -webkit-backface-visibility: hidden; }
16
  left: 0;
17
  width: 100%;
18
  height: 100%;
19
+ /*z-index: 1043;*/
20
+ /*ICG-Hack:: Higher z-index to make it visible on top */
21
+ z-index: 999999;
22
  position: fixed;
23
  outline: none !important;
24
  -webkit-backface-visibility: hidden; }
assets/js/admin.js CHANGED
@@ -44,21 +44,21 @@ jQuery(function() {
44
  display_message_themes(this_data[i]);
45
  };
46
 
47
- jQuery('.message-setting-fields').live('change', '.message_type', function() {
48
- display_message_themes(this);
 
49
  });
50
 
51
- jQuery('.message-setting-fields').live('change', '.message_theme', function() {
52
-
53
- var message_type = jQuery(this).find('.message_type').val();
54
- var message_theme = jQuery(this).find('.message_row.'+message_type).find('.message_theme').val();
55
- var message_thumb = jQuery(this).find('#message_theme_'+message_type).find('.'+message_theme).attr('style');
56
-
57
- jQuery(this).find('.message_row.'+message_type).find('.message_theme').next().find('.chosen-single span').attr('style',message_thumb);
58
 
59
  });
60
 
61
- jQuery('.message_image_button').live('click', function(event) {
62
  var that = this;
63
  window.send_to_editor = function(html) {
64
  imgurl = jQuery('img', html).attr('src');
@@ -69,7 +69,7 @@ jQuery(function() {
69
  return false;
70
  });
71
 
72
- jQuery('.message_headline_button').live('click', function() {
73
  var headline_key = jQuery(this).prev().attr('data-headline');
74
  var headline_max = icegram_writepanel_params.available_headlines.length;
75
  var new_headline_key = get_random_int( headline_key, 0, headline_max );
@@ -90,20 +90,19 @@ jQuery(function() {
90
  jQuery('#campaign_data .handlediv').hide();
91
 
92
  });
93
-
94
- jQuery('.message_delete').live( 'click', function() {
95
  jQuery(this).parent().parent().next().remove();
96
  jQuery(this).parent().parent().remove();
97
  hide_empty_campaign_message();
98
 
99
  });
100
 
101
- jQuery('.message_edit').live( 'click', function() {
102
  jQuery(this).parent().parent().next().toggle();
103
  jQuery(this).parent().parent().find('.message-title-text, .message-title-input').toggle();
104
  });
105
 
106
- jQuery('.message-title-input').live( 'change', function() {
107
  jQuery(this).prev().text(jQuery(this).val());
108
  });
109
 
@@ -123,14 +122,28 @@ jQuery(function() {
123
  });
124
  return terms;
125
  });
126
-
127
- jQuery('.cancel_parse_form').live('click', function(event) {
128
- tb_remove();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  jQuery(this).closest('form').get(0).reset();
130
  return false;
131
  });
132
 
133
- jQuery('.parse_form').live('click', function(event) {
134
  var that = this;
135
  var parent_node = jQuery(that).closest('form');
136
  var form_layout = jQuery(parent_node).find('.embed_form_layouts input[type=radio]:checked').val();
@@ -246,7 +259,12 @@ jQuery(function() {
246
  }
247
  form_container.find('.ig_detected_bot_fields').parent().css('display', 'none');
248
  form_object.append(form_container);
249
- tb_remove();
 
 
 
 
 
250
  // reset all fields of Embed form setting
251
  jQuery(that).closest('form').get(0).reset();
252
  window.send_to_editor(jQuery('<div/>').append(form_object).html());
@@ -254,10 +272,15 @@ jQuery(function() {
254
  });
255
 
256
 
257
- var message_rows = jQuery('.message-row').length;
258
- jQuery('.ajax_chosen_select_messages').chosen().on('change', function() {
 
 
 
 
 
259
  var message_id = jQuery(this).val();
260
- if( message_id == '' ) {
261
  jQuery(".ajax_chosen_select_messages").val('').trigger("chosen:updated");
262
  return;
263
  }
@@ -277,7 +300,8 @@ jQuery(function() {
277
  success: function(response) {
278
 
279
  message_rows++;
280
- jQuery('.messages-list .messages_list_table tbody').append(response.main);
 
281
  jQuery('.color-field').wpColorPicker();
282
  display_message_themes(jQuery('#'+response.id));
283
  jQuery(".ajax_chosen_select_messages").val('').trigger("chosen:updated");
@@ -287,12 +311,40 @@ jQuery(function() {
287
  hide_empty_campaign_message();
288
  jQuery('.message-setting-fields').trigger('change');
289
  jQuery(".tips, .help_tip").tipTip({'attribute' : 'data-tip'});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  QTags._buttonsInit();
 
 
 
 
 
291
  }
292
  });
293
  });
294
  //add local url
295
- jQuery('#add_local_url_row').live('click', function(e) {
296
  e.preventDefault();
297
  var row = add_url_row();
298
  if(jQuery('.local_url').find('.url_input_field').length){
@@ -303,7 +355,7 @@ jQuery(function() {
303
  }
304
 
305
  });
306
- jQuery('.delete-url').live('click', function(e) {
307
  jQuery(this).parent().remove();
308
  });
309
 
@@ -319,7 +371,7 @@ jQuery(function() {
319
  }
320
  }
321
 
322
- jQuery('select.ajax_chosen_select_messages').next('div').find('div.chosen-drop').live('click', function() {
323
  jQuery(this).closest('h3.hndle').trigger('click');
324
  });
325
 
@@ -328,7 +380,8 @@ jQuery(function() {
328
 
329
  if( jQuery('.message-row').length == 0 )
330
  return;
331
-
 
332
  // Change action
333
  params = jQuery("#post").serializeArray();
334
  params.push( {name: 'action', value: 'save_campaign_preview' });
@@ -394,7 +447,7 @@ jQuery(function() {
394
  showButtonPanel: true
395
  });
396
 
397
- jQuery('input.url_input_field').live('focusout', function() {
398
  var url = this;
399
  jQuery(url).parent().find('span#valid-field').removeClass('error');
400
  if(jQuery(url).data("option") !== 'undefine' && jQuery(url).data("option") == 'local_url' && jQuery(url).val() != '*'){
44
  display_message_themes(this_data[i]);
45
  };
46
 
47
+ jQuery('#campaign_data').on('change', '.message_type', function(e) {
48
+ var t = jQuery(e.target).parents('.message-setting-fields');
49
+ display_message_themes(t);
50
  });
51
 
52
+ jQuery('#campaign_data').on('change', '.message_theme', function(e) {
53
+ var t = jQuery(e.target).parents('.message-setting-fields');
54
+ var message_type = jQuery(t).find('.message_type').val();
55
+ var message_theme = jQuery(t).find('.message_row.'+message_type).find('.message_theme').val();
56
+ var message_thumb = jQuery(t).find('#message_theme_'+message_type).find('.'+message_theme).attr('style');
57
+ jQuery(t).find('.message_row.'+message_type).find('.message_theme').next().find('.chosen-single span').attr('style',message_thumb);
 
58
 
59
  });
60
 
61
+ jQuery('#campaign_data').on('click', '.message_image_button', function(event) {
62
  var that = this;
63
  window.send_to_editor = function(html) {
64
  imgurl = jQuery('img', html).attr('src');
69
  return false;
70
  });
71
 
72
+ jQuery('#campaign_data').on('click','.message_headline_button', function() {
73
  var headline_key = jQuery(this).prev().attr('data-headline');
74
  var headline_max = icegram_writepanel_params.available_headlines.length;
75
  var new_headline_key = get_random_int( headline_key, 0, headline_max );
90
  jQuery('#campaign_data .handlediv').hide();
91
 
92
  });
93
+ jQuery('#campaign_data').on('click','.message_delete', function() {
 
94
  jQuery(this).parent().parent().next().remove();
95
  jQuery(this).parent().parent().remove();
96
  hide_empty_campaign_message();
97
 
98
  });
99
 
100
+ jQuery('#campaign_data').on( 'click', '.message_edit' ,function() {
101
  jQuery(this).parent().parent().next().toggle();
102
  jQuery(this).parent().parent().find('.message-title-text, .message-title-input').toggle();
103
  });
104
 
105
+ jQuery('#campaign_data').on( 'change', '.message-title-input',function() {
106
  jQuery(this).prev().text(jQuery(this).val());
107
  });
108
 
122
  });
123
  return terms;
124
  });
125
+
126
+ jQuery(document).on('click', '#embed_form_but', function(event) {
127
+ jQuery.magnificPopup.open({
128
+ items: {
129
+ src: '#popup_container',
130
+ type: 'inline'
131
+ },
132
+ modal : true
133
+ });
134
+
135
+ });
136
+
137
+ jQuery(document).on('click', '.cancel_parse_form', function(event) {
138
+ jQuery.magnificPopup.close({ items: {
139
+ src: '#popup_container',
140
+ type: 'inline'
141
+ }});
142
  jQuery(this).closest('form').get(0).reset();
143
  return false;
144
  });
145
 
146
+ jQuery(document).on('click', '.parse_form' ,function(event) {
147
  var that = this;
148
  var parent_node = jQuery(that).closest('form');
149
  var form_layout = jQuery(parent_node).find('.embed_form_layouts input[type=radio]:checked').val();
259
  }
260
  form_container.find('.ig_detected_bot_fields').parent().css('display', 'none');
261
  form_object.append(form_container);
262
+
263
+ //closing a popup
264
+ jQuery.magnificPopup.close({ items: {
265
+ src: '#popup_container',
266
+ type: 'inline'
267
+ }});
268
  // reset all fields of Embed form setting
269
  jQuery(that).closest('form').get(0).reset();
270
  window.send_to_editor(jQuery('<div/>').append(form_object).html());
272
  });
273
 
274
 
275
+ //var message_rows = jQuery(this).parent().siblings('.campaign_target_rules_panel').find('.message-row').length;
276
+ jQuery('.ajax_chosen_select_messages').chosen();
277
+ jQuery('#campaign_data').on('change', '.ajax_chosen_select_messages' , function() {
278
+ var newSettings = jQuery.extend( {}, tinyMCEPreInit.mceInit[ 'content' ] );
279
+ var newQTS = jQuery.extend( {}, tinyMCEPreInit.qtInit[ 'content' ] );
280
+ var parent_campaign_box = jQuery(this).parent().siblings('.campaign_target_rules_panel');
281
+ var message_rows = jQuery(parent_campaign_box).find('.message-row').length;
282
  var message_id = jQuery(this).val();
283
+ if( message_id == '' ) {
284
  jQuery(".ajax_chosen_select_messages").val('').trigger("chosen:updated");
285
  return;
286
  }
300
  success: function(response) {
301
 
302
  message_rows++;
303
+ //jQuery('.messages-list .messages_list_table tbody').append(response.main);
304
+ jQuery(parent_campaign_box).find('.messages-list .messages_list_table tbody').append(response.main);
305
  jQuery('.color-field').wpColorPicker();
306
  display_message_themes(jQuery('#'+response.id));
307
  jQuery(".ajax_chosen_select_messages").val('').trigger("chosen:updated");
311
  hide_empty_campaign_message();
312
  jQuery('.message-setting-fields').trigger('change');
313
  jQuery(".tips, .help_tip").tipTip({'attribute' : 'data-tip'});
314
+ // text editor issue fix
315
+ if ( typeof( tinyMCEPreInit.mceInit[ 'edit'+response.id ] ) === 'undefined' ) {
316
+ for ( _prop in newSettings ) {
317
+ if ( 'string' === typeof( newSettings[_prop] ) ) {
318
+ if(_prop !== 'content_css'){
319
+ newSettings[_prop] = newSettings[_prop].replace( new RegExp( 'content', 'g' ), 'edit'+response.id );
320
+ }
321
+ }
322
+ }
323
+ tinyMCEPreInit.mceInit[ 'edit'+response.id ] = newSettings;
324
+ }
325
+ if ( typeof( tinyMCEPreInit.qtInit[ 'edit'+response.id ] ) === 'undefined' ) {
326
+ for ( _prop in newQTS ) {
327
+ if ( 'string' === typeof( newQTS[_prop] ) ) {
328
+ if(_prop !== 'content_css'){
329
+ newQTS[_prop] = newQTS[_prop].replace( new RegExp( 'content', 'g' ), 'edit'+response.id );
330
+ }
331
+ }
332
+ }
333
+ tinyMCEPreInit.qtInit[ 'edit'+response.id ] = newQTS;
334
+ }
335
+ tinyMCE.init({ id : tinyMCEPreInit.mceInit[ 'edit'+response.id ]});
336
+ quicktags({id : 'edit'+response.id});
337
  QTags._buttonsInit();
338
+ if(jQuery('#wp-edit'+response.id+'-wrap').hasClass('tmce-active')){
339
+ jQuery('#edit'+response.id+'-tmce').click();
340
+ }else{
341
+ jQuery('#edit'+response.id+'-html').click();
342
+ }
343
  }
344
  });
345
  });
346
  //add local url
347
+ jQuery('#campaign_target_rules').on('click', '#add_local_url_row' ,function(e) {
348
  e.preventDefault();
349
  var row = add_url_row();
350
  if(jQuery('.local_url').find('.url_input_field').length){
355
  }
356
 
357
  });
358
+ jQuery('#campaign_target_rules').on('click', '.delete-url',function(e) {
359
  jQuery(this).parent().remove();
360
  });
361
 
371
  }
372
  }
373
 
374
+ jQuery('select.ajax_chosen_select_messages').next('div').on('click', 'div.chosen-drop' ,function() {
375
  jQuery(this).closest('h3.hndle').trigger('click');
376
  });
377
 
380
 
381
  if( jQuery('.message-row').length == 0 )
382
  return;
383
+ // trigger event for saving visual content
384
+ tinyMCE.triggerSave();
385
  // Change action
386
  params = jQuery("#post").serializeArray();
387
  params.push( {name: 'action', value: 'save_campaign_preview' });
447
  showButtonPanel: true
448
  });
449
 
450
+ jQuery('#campaign_target_rules').on('focusout','input.url_input_field',function() {
451
  var url = this;
452
  jQuery(url).parent().find('span#valid-field').removeClass('error');
453
  if(jQuery(url).data("option") !== 'undefine' && jQuery(url).data("option") == 'local_url' && jQuery(url).val() != '*'){
assets/js/icegram.js CHANGED
@@ -40,13 +40,16 @@
40
  if (this.message_data.length > 0) {
41
  jQuery.each( this.message_data, function ( i, v ) {
42
  try {
43
- //check cookies in js
44
- if(v['retargeting'] == 'yes' && jQuery.cookie('icegram_messages_shown_'+v['id']) == 1){
45
- return;
46
- }
47
- if(v['retargeting_clicked'] == 'yes' && jQuery.cookie('icegram_messages_clicked_'+v['id']) == 1){
48
- return;
49
- }
 
 
 
50
 
51
  var m = null;
52
  var classname_suffix = v['type'].split('-').join(' ').ucwords().split(' ').join('_');
@@ -141,7 +144,7 @@
141
  }
142
  }
143
  Icegram.prototype.submit_tracking_data = function ( ev, params ) {
144
- if (this.tracking_data.length > 0) {
145
  var params = {
146
  method: 'POST',
147
  url: this.data.ajax_url,
40
  if (this.message_data.length > 0) {
41
  jQuery.each( this.message_data, function ( i, v ) {
42
  try {
43
+ // dont check cookies in preview mode
44
+ if(window.location.href.indexOf("campaign_preview_id") == -1){
45
+ //check cookies in js
46
+ if(v['retargeting'] == 'yes' && jQuery.cookie('icegram_messages_shown_'+v['id']) == 1){
47
+ return;
48
+ }
49
+ if(v['retargeting_clicked'] == 'yes' && jQuery.cookie('icegram_messages_clicked_'+v['id']) == 1){
50
+ return;
51
+ }
52
+ }
53
 
54
  var m = null;
55
  var classname_suffix = v['type'].split('-').join(' ').ucwords().split(' ').join('_');
144
  }
145
  }
146
  Icegram.prototype.submit_tracking_data = function ( ev, params ) {
147
+ if (this.tracking_data.length > 0 && window.location.href.indexOf("campaign_preview_id") == -1) {
148
  var params = {
149
  method: 'POST',
150
  url: this.data.ajax_url,
classes/class-icegram-campaign-admin.php CHANGED
@@ -14,9 +14,10 @@ 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
  //duplicate campaign
21
  add_filter( 'post_row_actions', array(&$this , 'add_campaign_action'), 10, 2 );
22
  add_action('admin_init', array(&$this ,'duplicate_campaign') ,10, 1);
@@ -33,6 +34,13 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
33
  );
34
 
35
  }
 
 
 
 
 
 
 
36
 
37
  // Initialize campaign metabox
38
  function add_campaigns_metaboxes() {
@@ -69,7 +77,9 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
69
  ?>
70
  <tbody>
71
  <?php
72
- $messages = get_post_meta( $post->ID, 'messages', true );
 
 
73
  if ( !empty( $messages ) ) {
74
  foreach ( $messages as $row => $message ) {
75
  $message_title = get_the_title( $message['id'] );
@@ -86,8 +96,8 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
86
  <input type="text" class="message-title-input" name="message_data[<?php echo $message['id']; ?>][post_title]" value="<?php echo $message_title; ?>" placeholder="<?php echo __( 'Give this message a name for your own reference', 'icegram' ); ?>" style="display: none;">
87
  </td>
88
  <td class="message_seconds">
89
- <input type="hidden" name="messages[<?php echo $row; ?>][id]" value="<?php echo $message['id']; ?>" />
90
- <input type="number" class="seconds-text" name="messages[<?php echo $row; ?>][time]" min="-1" value="<?php echo ( !empty( $message['time'] ) ) ? $message['time'] : 0; ?>" size="3" />
91
  <?php _e( ' sec', 'icegram' )?>
92
  </td>
93
  <td class="action_links">
@@ -119,6 +129,10 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
119
  <?php
120
  }
121
 
 
 
 
 
122
  // Campaign targeting rules metabox
123
  function campaign_target_rules_content() {
124
  global $post;
@@ -471,6 +485,7 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
471
  }
472
 
473
  ob_start();
 
474
  ?>
475
  <tr class="form-field message-row" value="<?php echo $message_id; ?>">
476
  <td class="message_header">
@@ -481,8 +496,8 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
481
  <input type="text" class="message-title-input" name="message_data[<?php echo $message_id; ?>][post_title]" value="<?php echo $message_title; ?>" placeholder="<?php echo __( 'Give this message a name for your own reference', 'icegram' ); ?>">
482
  </td>
483
  <td class="message_seconds">
484
- <input type="hidden" name="messages[<?php echo $_POST['row']; ?>][id]" value="<?php echo $message_id; ?>" />
485
- <input type="number" class="seconds-text" name="messages[<?php echo $_POST['row']; ?>][time]" min="0" value="0" size="3" /><?php _e( ' sec', 'icegram' )?>
486
  </td>
487
  <td class="action_links">
488
  <span class="actions message_edit" title="<?php _e( 'Edit Message', 'icegram' ); ?>" ></span>
@@ -596,7 +611,7 @@ if ( !class_exists( 'Icegram_Campaign_Admin' ) ) {
596
  if ( !current_user_can( 'edit_post', $_POST['post_ID'] ) ) die();
597
 
598
  if( !empty( $_POST['messages'] ) ) {
599
-
600
  update_post_meta( $_POST['post_ID'], 'campaign_preview', $_POST['messages'] ) ;
601
 
602
  foreach ( (array) $_POST['message_data'] as $message_id => $message_data ) {
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 );
21
  //duplicate campaign
22
  add_filter( 'post_row_actions', array(&$this , 'add_campaign_action'), 10, 2 );
23
  add_action('admin_init', array(&$this ,'duplicate_campaign') ,10, 1);
34
  );
35
 
36
  }
37
+ public static function getInstance(){
38
+ static $ig_campaign_admin = null;
39
+ if (null === $ig_campaign_admin) {
40
+ $ig_campaign_admin = new Icegram_Campaign_Admin();
41
+ }
42
+ return $ig_campaign_admin;
43
+ }
44
 
45
  // Initialize campaign metabox
46
  function add_campaigns_metaboxes() {
77
  ?>
78
  <tbody>
79
  <?php
80
+ $messages = array();
81
+ $messages = apply_filters('icegram_campaign_messages' , $messages ,$post->ID);
82
+ $icegram_message_meta_key = apply_filters('icegram_message_meta_key' , 'messages');
83
  if ( !empty( $messages ) ) {
84
  foreach ( $messages as $row => $message ) {
85
  $message_title = get_the_title( $message['id'] );
96
  <input type="text" class="message-title-input" name="message_data[<?php echo $message['id']; ?>][post_title]" value="<?php echo $message_title; ?>" placeholder="<?php echo __( 'Give this message a name for your own reference', 'icegram' ); ?>" style="display: none;">
97
  </td>
98
  <td class="message_seconds">
99
+ <input type="hidden" name="<?php echo $icegram_message_meta_key .'['.$row; ?>][id]" value="<?php echo $message['id']; ?>" />
100
+ <input type="number" class="seconds-text" name="<?php echo $icegram_message_meta_key .'['.$row; ?>][time]" min="-1" value="<?php echo ( !empty( $message['time'] ) ) ? $message['time'] : 0; ?>" size="3" />
101
  <?php _e( ' sec', 'icegram' )?>
102
  </td>
103
  <td class="action_links">
129
  <?php
130
  }
131
 
132
+ function get_icegram_campaign_messages($messages , $campaign_id){
133
+ $messages = get_post_meta($campaign_id , 'messages' ,true);
134
+ return $messages;
135
+ }
136
  // Campaign targeting rules metabox
137
  function campaign_target_rules_content() {
138
  global $post;
485
  }
486
 
487
  ob_start();
488
+ $icegram_message_meta_key = apply_filters('icegram_message_meta_key' , 'messages');
489
  ?>
490
  <tr class="form-field message-row" value="<?php echo $message_id; ?>">
491
  <td class="message_header">
496
  <input type="text" class="message-title-input" name="message_data[<?php echo $message_id; ?>][post_title]" value="<?php echo $message_title; ?>" placeholder="<?php echo __( 'Give this message a name for your own reference', 'icegram' ); ?>">
497
  </td>
498
  <td class="message_seconds">
499
+ <input type="hidden" name="<?php echo $icegram_message_meta_key .'['.$_POST['row']; ?>][id]" value="<?php echo $message_id; ?>" />
500
+ <input type="number" class="seconds-text" name="<?php echo $icegram_message_meta_key .'['.$_POST['row']; ?>][time]" min="0" value="0" size="3" /><?php _e( ' sec', 'icegram' )?>
501
  </td>
502
  <td class="action_links">
503
  <span class="actions message_edit" title="<?php _e( 'Edit Message', 'icegram' ); ?>" ></span>
611
  if ( !current_user_can( 'edit_post', $_POST['post_ID'] ) ) die();
612
 
613
  if( !empty( $_POST['messages'] ) ) {
614
+
615
  update_post_meta( $_POST['post_ID'], 'campaign_preview', $_POST['messages'] ) ;
616
 
617
  foreach ( (array) $_POST['message_data'] as $message_id => $message_data ) {
classes/class-icegram-message-admin.php CHANGED
@@ -47,18 +47,21 @@ if ( !class_exists( 'Icegram_Message_Admin' ) ) {
47
  $message_types[] = $type;
48
  }
49
  }
50
- echo $out = '<a href="#TB_inline?width=500&height=520&inlineId=popup_container" data-editor="'.$editor_id.'" class="'.implode( ' ', $message_types).' thickbox button" id="embed_form_but" title="'.__('Add an optin / subscription form' , 'icegram' ).'">'.__('Embed Form' , 'icegram' ).'</a>';
51
  }
52
  }
53
 
54
  function embed_form_popup_content(){
 
 
55
  ?>
56
- <div id="popup_container" style="display:none;">
57
-
 
 
58
  <form id="embed_form" action="#">
59
- <!-- <h3 ><?php _e('Add an optin / subscription form', 'icegram' ); ?></h3> -->
60
  <p>
61
- <textarea rows="10" autocomplete="off" cols="65" name="form_data" style="width:90%" id="form_data" value="" placeholder="<?php _e('Paste HTML code of your form here...', 'icegram' ); ?>"></textarea>
62
  </p>
63
  <p class="use_cta_check">
64
  <label><input type="checkbox" name="use_cta_to_submit" value="" checked> <?php _e('Use message\'s Call to Action button to submit this form' , 'icegram' ); ?></label>
@@ -290,6 +293,7 @@ if ( !class_exists( 'Icegram_Message_Admin' ) ) {
290
  );
291
  ?>
292
  <p class="message_row <?php echo implode( ' ', $settings['message'] )?>">
 
293
  <label for="message_body" class="message_body message_label"><strong><?php _e( 'Message Body', 'icegram' ); ?></strong></label>
294
  <?php $message = ( !empty( $message_data['message'] ) ) ? $message_data['message'] : ''; ?>
295
  <?php wp_editor( $message, 'edit'.$message_id, $editor_args ); ?>
@@ -344,15 +348,8 @@ if ( !class_exists( 'Icegram_Message_Admin' ) ) {
344
  <?php
345
 
346
  if( !empty( $action['message_id'] ) ) {
347
-
348
  ?></div>
349
  <?php
350
- if( !empty( $action['new_message_row'] ) && $action['new_message_row'] ) {
351
- \_WP_Editors::enqueue_scripts();
352
- // print_footer_scripts();
353
- \_WP_Editors::editor_js();
354
- }
355
-
356
  } else {
357
 
358
  ?>
47
  $message_types[] = $type;
48
  }
49
  }
50
+ echo $out = '<a href="#popup_container" class="'.implode( ' ', $message_types).' button" id="embed_form_but">'.__('Embed Form' , 'icegram' ).'</a>';
51
  }
52
  }
53
 
54
  function embed_form_popup_content(){
55
+ $screen = get_current_screen();
56
+ if ( !in_array( $screen->id, array( 'ig_campaign', 'ig_message' ), true ) ) return;
57
  ?>
58
+ <div id="popup_container" class="mfp-hide" >
59
+ <h3><?php _e('Add an optin / subscription form', 'icegram' ); ?>
60
+ <a id="embed_howto_link" target="_blank" href="http://www.icegram.com/icegram_form_integration/" ><?php _e('Need help?', 'icegram' ); ?></a>
61
+ </h3>
62
  <form id="embed_form" action="#">
 
63
  <p>
64
+ <textarea rows="10" autocomplete="off" cols="65" name="form_data" id="form_data" value="" placeholder="<?php _e('Paste HTML code of your form here...', 'icegram' ); ?>"></textarea>
65
  </p>
66
  <p class="use_cta_check">
67
  <label><input type="checkbox" name="use_cta_to_submit" value="" checked> <?php _e('Use message\'s Call to Action button to submit this form' , 'icegram' ); ?></label>
293
  );
294
  ?>
295
  <p class="message_row <?php echo implode( ' ', $settings['message'] )?>">
296
+ <style type="text/css">.wp-editor-tools:after {display: inline-block !important; }</style>
297
  <label for="message_body" class="message_body message_label"><strong><?php _e( 'Message Body', 'icegram' ); ?></strong></label>
298
  <?php $message = ( !empty( $message_data['message'] ) ) ? $message_data['message'] : ''; ?>
299
  <?php wp_editor( $message, 'edit'.$message_id, $editor_args ); ?>
348
  <?php
349
 
350
  if( !empty( $action['message_id'] ) ) {
 
351
  ?></div>
352
  <?php
 
 
 
 
 
 
353
  } else {
354
 
355
  ?>
classes/mobile-detect.php ADDED
@@ -0,0 +1,1183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,11 +3,11 @@
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.4
7
  * Author: icegram
8
  * Author URI: http://www.icegram.com/
9
  *
10
- * Copyright (c) 2014-2015 Icegram
11
  * License: GPLv3
12
  * License URI: http://www.gnu.org/licenses/gpl-3.0.html
13
  *
@@ -34,7 +34,7 @@ class Icegram {
34
 
35
  function __construct() {
36
 
37
- $this->version = "1.8.4";
38
  $this->shortcode_instances = array();
39
  $this->mode = 'local';
40
  $this->plugin_url = untrailingslashit( plugins_url( '/', __FILE__ ) );
@@ -42,7 +42,7 @@ class Icegram {
42
  $this->include_classes();
43
 
44
  if( is_admin() && current_user_can( 'manage_options' ) ) {
45
- new Icegram_Campaign_Admin();
46
  $ig_message_admin = Icegram_Message_Admin::getInstance();
47
  add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_admin_styles_and_scripts' ) );
48
  add_action( 'admin_print_styles', array( &$this, 'remove_preview_button' ) );
@@ -671,7 +671,10 @@ class Icegram {
671
  wp_enqueue_script( 'icegram_writepanel' );
672
  wp_enqueue_script( 'icegram_ajax-chosen' );
673
  wp_enqueue_script( 'icegram_tiptip' );
674
- wp_enqueue_script( 'thickbox' );
 
 
 
675
 
676
  $icegram_writepanel_params = array ( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'search_message_nonce' => wp_create_nonce( "search-messages" ) );
677
  $this->available_headlines = apply_filters( 'icegram_available_headlines', array() );
@@ -679,69 +682,28 @@ class Icegram {
679
 
680
  wp_localize_script( 'icegram_writepanel', 'icegram_writepanel_params', $icegram_writepanel_params );
681
 
682
- wp_enqueue_style( 'thickbox' );
683
  wp_enqueue_style( 'dashicons' );
684
  wp_enqueue_style( 'wp-color-picker' );
685
  wp_enqueue_style( 'icegram_admin_styles', $this->plugin_url . '/assets/css/admin.css', array(), $this->version );
686
  wp_enqueue_style( 'icegram_jquery-ui-style', $this->plugin_url . '/assets/css/jquery-ui.min.css', array(), $this->version );
687
  wp_enqueue_style( 'icegram_chosen_styles', $this->plugin_url . '/assets/css/chosen.min.css', array(), $this->version );
 
688
 
689
  if ( !wp_script_is( 'jquery-ui-datepicker' ) ) {
690
  wp_enqueue_script( 'jquery-ui-datepicker' );
691
  }
692
 
693
-
694
  }
695
 
696
  public static function get_platform() {
697
- $platform = '';
698
- $user_agent = trim(strtolower($_SERVER['HTTP_USER_AGENT']));
699
- $pattern = '/(android\s\d|blackberry|ip(hone|ad|od)|iemobile|webos|palm|symbian|kindle|windows|win64|wow64|macintosh|intel\smac\sos\sx|ppx\smac\sos\sx|googlebot|googlebot-mobile)/';
700
- if ( preg_match( $pattern, $user_agent, $matches ) ) {
701
- $platform = $matches[0];
 
702
  }
703
-
704
- switch ( $platform ) {
705
-
706
- /* phones / smartphones */
707
- case 'android 1':
708
- case 'android 2':
709
- case 'blackberry':
710
- case 'iphone':
711
- case 'ipod':
712
- case 'iemobile':
713
- case 'webos':
714
- case 'palm':
715
- case 'symbian':
716
- case 'googlebot-mobile':
717
- $platform = 'mobile';
718
- break;
719
-
720
- /* tablets */
721
- case 'android 3':
722
- case 'android 4':
723
- case 'ipad':
724
- case 'kindle':
725
- $platform = 'tablet';
726
- break;
727
-
728
- /* desktops / laptops */
729
- case 'windows':
730
- case 'win64':
731
- case 'wow64':
732
- case 'macintosh':
733
- case 'ppx mac os x':
734
- case 'intel mac os x':
735
- case 'googlebot':
736
- $platform = 'laptop';
737
- break;
738
-
739
- /* in case nothing else matches */
740
- default:
741
- $platform = 'laptop';
742
- break;
743
- }
744
- return $platform;
745
  }
746
 
747
  function get_message_data( $message_ids = array(), $preview = false ) {
@@ -788,7 +750,7 @@ class Icegram {
788
  $message_ids = array_filter(array_unique( (array) $message_ids));
789
 
790
  if ( !empty( $campaign_ids ) ) {
791
- $valid_campaigns = $this->get_valid_campaigns( $campaign_ids, true );
792
  }
793
  // When skip_others is true, we won't load campaigns / messages from db
794
  if (!$skip_others && !$preview_mode) {
@@ -862,7 +824,7 @@ class Icegram {
862
  return $valid_messages;
863
  }
864
 
865
- function get_valid_campaigns( $campaign_ids = array(), $skip_page_check = false ) {
866
  global $wpdb;
867
 
868
  if ( empty( $campaign_ids ) ) {
@@ -879,8 +841,7 @@ class Icegram {
879
  $valid_campaigns = array();
880
  foreach ( (array) $campaign_ids as $campaign_id ) {
881
  $campaign = new Icegram_Campaign( $campaign_id );
882
-
883
- if ( $campaign->is_valid( array('skip_page_check' => $skip_page_check) ) ) {
884
  $valid_campaigns[$campaign_id] = $campaign;
885
  } else {
886
  // Campgain is invalid!
@@ -1233,19 +1194,17 @@ class Icegram {
1233
 
1234
  function remove_preview_button() {
1235
  global $post_type;
1236
-
1237
  if( $post_type == 'ig_message' || $post_type == 'ig_campaign' ) {
1238
-
1239
  ?>
1240
- <style>
1241
- #preview-action { display:none; }
1242
- </style>
 
1243
  <?php
1244
-
1245
  }
1246
-
1247
  }
1248
 
 
1249
  function remove_row_actions( $actions, $post ) {
1250
 
1251
  if ( empty( $post->post_type ) || ( $post->post_type != 'ig_campaign' && $post->post_type != 'ig_message' ) ) return $actions;
@@ -1429,6 +1388,7 @@ function initialize_icegram() {
1429
  load_plugin_textdomain( 'icegram', false, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );
1430
 
1431
  $icegram = new Icegram();
 
1432
  }
1433
 
1434
  add_action( 'plugins_loaded', 'initialize_icegram' );
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
  *
10
+ * Copyright (c) 2014-15 Icegram
11
  * License: GPLv3
12
  * License URI: http://www.gnu.org/licenses/gpl-3.0.html
13
  *
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__ ) );
42
  $this->include_classes();
43
 
44
  if( is_admin() && current_user_can( 'manage_options' ) ) {
45
+ $ig_campaign_admin = Icegram_Campaign_Admin::getInstance();
46
  $ig_message_admin = Icegram_Message_Admin::getInstance();
47
  add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_admin_styles_and_scripts' ) );
48
  add_action( 'admin_print_styles', array( &$this, 'remove_preview_button' ) );
671
  wp_enqueue_script( 'icegram_writepanel' );
672
  wp_enqueue_script( 'icegram_ajax-chosen' );
673
  wp_enqueue_script( 'icegram_tiptip' );
674
+ wp_register_script( 'magnific_popup_js', $this->plugin_url . '/assets/js/magnific-popup.js', array ( 'jquery' ), $this->version, true);
675
+ if( !wp_script_is( 'magnific_popup_js' ) ) {
676
+ wp_enqueue_script( 'magnific_popup_js' );
677
+ }
678
 
679
  $icegram_writepanel_params = array ( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'search_message_nonce' => wp_create_nonce( "search-messages" ) );
680
  $this->available_headlines = apply_filters( 'icegram_available_headlines', array() );
682
 
683
  wp_localize_script( 'icegram_writepanel', 'icegram_writepanel_params', $icegram_writepanel_params );
684
 
 
685
  wp_enqueue_style( 'dashicons' );
686
  wp_enqueue_style( 'wp-color-picker' );
687
  wp_enqueue_style( 'icegram_admin_styles', $this->plugin_url . '/assets/css/admin.css', array(), $this->version );
688
  wp_enqueue_style( 'icegram_jquery-ui-style', $this->plugin_url . '/assets/css/jquery-ui.min.css', array(), $this->version );
689
  wp_enqueue_style( 'icegram_chosen_styles', $this->plugin_url . '/assets/css/chosen.min.css', array(), $this->version );
690
+ wp_enqueue_style( 'magnific_popup_css', $this->plugin_url . '/assets/css/magnific-popup.css', array(), $this->version );
691
 
692
  if ( !wp_script_is( 'jquery-ui-datepicker' ) ) {
693
  wp_enqueue_script( 'jquery-ui-datepicker' );
694
  }
695
 
 
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';
703
+ }else if($mobile_detect->isTablet()){
704
+ return 'tablet';
705
  }
706
+ return 'laptop';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
  }
708
 
709
  function get_message_data( $message_ids = array(), $preview = false ) {
750
  $message_ids = array_filter(array_unique( (array) $message_ids));
751
 
752
  if ( !empty( $campaign_ids ) ) {
753
+ $valid_campaigns = $this->get_valid_campaigns( $campaign_ids, true ,true);
754
  }
755
  // When skip_others is true, we won't load campaigns / messages from db
756
  if (!$skip_others && !$preview_mode) {
824
  return $valid_messages;
825
  }
826
 
827
+ function get_valid_campaigns( $campaign_ids = array(), $skip_page_check = false ,$preview_mode = false) {
828
  global $wpdb;
829
 
830
  if ( empty( $campaign_ids ) ) {
841
  $valid_campaigns = array();
842
  foreach ( (array) $campaign_ids as $campaign_id ) {
843
  $campaign = new Icegram_Campaign( $campaign_id );
844
+ if ( $preview_mode || $campaign->is_valid( array('skip_page_check' => $skip_page_check) ) ) {
 
845
  $valid_campaigns[$campaign_id] = $campaign;
846
  } else {
847
  // Campgain is invalid!
1194
 
1195
  function remove_preview_button() {
1196
  global $post_type;
 
1197
  if( $post_type == 'ig_message' || $post_type == 'ig_campaign' ) {
 
1198
  ?>
1199
+ <style type="text/css">
1200
+ #message.updated.below-h2{ display: none; }
1201
+ #preview-action { display:none; }
1202
+ </style>
1203
  <?php
 
1204
  }
 
1205
  }
1206
 
1207
+
1208
  function remove_row_actions( $actions, $post ) {
1209
 
1210
  if ( empty( $post->post_type ) || ( $post->post_type != 'ig_campaign' && $post->post_type != 'ig_message' ) ) return $actions;
1388
  load_plugin_textdomain( 'icegram', false, dirname( plugin_basename( __FILE__ ) ) . '/lang/' );
1389
 
1390
  $icegram = new Icegram();
1391
+ do_action('icegram_loaded');
1392
  }
1393
 
1394
  add_action( 'plugins_loaded', 'initialize_icegram' );
message-types/popup/main.js CHANGED
@@ -32,8 +32,7 @@
32
  },
33
  showCloseBtn :false
34
  });
35
- jQuery('.mfp-content').removeClass().addClass('mfp-content');
36
- jQuery('.mfp-content').addClass(self.data.theme).addClass('ig_popup');
37
  silent !== true && this.track( 'shown' );
38
  };
39
 
32
  },
33
  showCloseBtn :false
34
  });
35
+ jQuery('.mfp-content').removeClass().addClass('mfp-content ig_popup ' + self.data.theme);
 
36
  silent !== true && this.track( 'shown' );
37
  };
38
 
message-types/popup/themes/air-mail.css CHANGED
@@ -137,4 +137,4 @@ div.ig_popup.air-mail input[type="button"]:active{
137
  .ig_popup.air-mail .ig_button, .ig_popup.air-mail input[type="submit"], .ig_popup.air-mail input[type="button"]{
138
  width: 90% !important;
139
  }
140
- }
137
  .ig_popup.air-mail .ig_button, .ig_popup.air-mail input[type="submit"], .ig_popup.air-mail input[type="button"]{
138
  width: 90% !important;
139
  }
140
+ }
message-types/popup/themes/inspire.css CHANGED
@@ -68,7 +68,6 @@
68
  width: 90% !important;
69
  }
70
  }
71
-
72
  @media only screen and (min-device-width: 320px) and (max-device-width: 359px) {
73
  .ig_popup.inspire .ig_button, .ig_popup.inspire input[type="submit"], .ig_popup.inspire input[type="button"]{
74
  width: 90% !important;
68
  width: 90% !important;
69
  }
70
  }
 
71
  @media only screen and (min-device-width: 320px) and (max-device-width: 359px) {
72
  .ig_popup.inspire .ig_button, .ig_popup.inspire input[type="submit"], .ig_popup.inspire input[type="button"]{
73
  width: 90% !important;
message-types/popup/themes/persuade.css CHANGED
@@ -76,4 +76,4 @@
76
  -webkit-box-shadow: none;
77
  box-shadow: none;
78
  background: hsl(1, 83%, 62%);
79
- }
76
  -webkit-box-shadow: none;
77
  box-shadow: none;
78
  background: hsl(1, 83%, 62%);
79
+ }
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.4
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -46,9 +46,16 @@ Determine where, when and how to show on-site marketing messages to visitors. Us
46
  * Embed subscription forms from **mailing list service of your choice**
47
  * Use short codes **other plugins** provide – embed forms, widgets, social sharing buttons, twitter feed…
48
  * Add **inline JavaScript or CSS** for customizations
49
- * Compatible with WPML for multilingual, translated messages
50
  * **Full HTML** editing capability for maximum control
51
 
 
 
 
 
 
 
 
52
  = Icegram is a Good Neighbour =
53
  Icegram works with most WordPress plugins and themes. It can integrate with all mailing list / newsletter / optin / CRM / form services that provide embeddable HTML form too.
54
 
@@ -67,7 +74,8 @@ Icegram is an open platform. The core is already powerful, but you can do even m
67
  * Exit Intent, Scroll Position, Time Delay, JavaScript, Another Message... Show and Hide messages based on user behavior
68
  * Geo Targeting - based on visitor's location - continent, country, city, region or IP address
69
  * CTA Actions - Open link, in new window, submit form, AJAX submission, show another message, hide this message... New actions for CTA buttons
70
- * Additional message types - Overlay, Inline, Sidebars, Ribbons, Stickies, Badges...
 
71
 
72
  = Ok, there is even more =
73
  There is no end to Icegram's awesomeness!!
@@ -145,6 +153,9 @@ Contact Us, provide as much detail of the problem as you can. We will try to sol
145
  3. Editing an Icegram campaign - add multiple messages, edit them, targeting rules, full HTML editor, themes and more
146
 
147
  == Upgrade Notice ==
 
 
 
148
  = 1.8.4 =
149
  Popups are now responsive! Plus Visual Composer compatibility and other fixes.
150
 
@@ -192,6 +203,13 @@ Initial Release
192
 
193
 
194
  == Changelog ==
 
 
 
 
 
 
 
195
  = 1.8.4 =
196
  * New: Popup messages work beautifully on mobile / small devices now!
197
  * Fix: Timing problems with popups triggered via Behavior Trigger addon.
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
 
46
  * Embed subscription forms from **mailing list service of your choice**
47
  * Use short codes **other plugins** provide – embed forms, widgets, social sharing buttons, twitter feed…
48
  * Add **inline JavaScript or CSS** for customizations
49
+ * Target campaigns on different pages - with Wildcard URLs, page selection and more...
50
  * **Full HTML** editing capability for maximum control
51
 
52
+ = Works with Multi Site, WPML and even non WordPress sites =
53
+ Icegram and its addons are WordPress Multisite compatible. Each site becomes an independent Icegram installation.
54
+
55
+ Icegram is also *WPML Compatible*. You can show multilingual messages in different languages as you wish.
56
+
57
+ And, as a matter of fact you can show Icegram campaign and messages on non WordPress sites too! Use the premium Icegram Remote addon, and you can show Icegram campaigns anywhere.
58
+
59
  = Icegram is a Good Neighbour =
60
  Icegram works with most WordPress plugins and themes. It can integrate with all mailing list / newsletter / optin / CRM / form services that provide embeddable HTML form too.
61
 
74
  * Exit Intent, Scroll Position, Time Delay, JavaScript, Another Message... Show and Hide messages based on user behavior
75
  * Geo Targeting - based on visitor's location - continent, country, city, region or IP address
76
  * CTA Actions - Open link, in new window, submit form, AJAX submission, show another message, hide this message... New actions for CTA buttons
77
+ * Remote - Show Icegram messages on any site - WordPress or otherwise
78
+ * Additional message types - Overlay, Inline, Sidebars, Interstitial, Ribbons, Stickies, Badges...
79
 
80
  = Ok, there is even more =
81
  There is no end to Icegram's awesomeness!!
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
+
159
  = 1.8.4 =
160
  Popups are now responsive! Plus Visual Composer compatibility and other fixes.
161
 
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
209
+ * Fix: Popup hiding behind header for some themes
210
+ * Fix: Device targeting rule not working on newer devices
211
+ * Fix: Preview working only after Publishing
212
+
213
  = 1.8.4 =
214
  * New: Popup messages work beautifully on mobile / small devices now!
215
  * Fix: Timing problems with popups triggered via Behavior Trigger addon.