Breeze – WordPress Cache Plugin - Version 1.0.0

Version Description

  • Add : First Beta release

=

Download this release

Release Info

Developer adeelkhan
Plugin Icon 128x128 Breeze – WordPress Cache Plugin
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (51) hide show
  1. assets/css/style.css +97 -0
  2. assets/images/hdr-logo.png +0 -0
  3. assets/js/breeze-backend.js +130 -0
  4. assets/js/breeze-configuration.js +54 -0
  5. breeze.php +106 -0
  6. inc/breeze-admin.php +324 -0
  7. inc/breeze-configuration.php +541 -0
  8. inc/cache/Mobile-Detect-2.8.25/LICENSE.txt +48 -0
  9. inc/cache/Mobile-Detect-2.8.25/Mobile_Detect.json +1 -0
  10. inc/cache/Mobile-Detect-2.8.25/Mobile_Detect.php +1460 -0
  11. inc/cache/Mobile-Detect-2.8.25/README.md +283 -0
  12. inc/cache/Mobile-Detect-2.8.25/composer.json +28 -0
  13. inc/cache/Mobile-Detect-2.8.25/export/exportToJSON.php +67 -0
  14. inc/cache/Mobile-Detect-2.8.25/namespaced/Detection/MobileDetect.php +22 -0
  15. inc/cache/Mobile-Detect-2.8.25/ruleset.xml +187 -0
  16. inc/cache/config-cache.php +232 -0
  17. inc/cache/execute-cache.php +377 -0
  18. inc/cache/purge-cache.php +172 -0
  19. inc/cache/purge-per-time.php +116 -0
  20. inc/cache/purge-varnish.php +338 -0
  21. inc/cdn-integration/breeze-cdn-integration.php +58 -0
  22. inc/cdn-integration/breeze-cdn-rewrite.php +119 -0
  23. inc/minification/breeze-minification-base.php +320 -0
  24. inc/minification/breeze-minification-cache.php +256 -0
  25. inc/minification/breeze-minification-html.php +89 -0
  26. inc/minification/breeze-minification-scripts.php +504 -0
  27. inc/minification/breeze-minification-styles.php +809 -0
  28. inc/minification/breeze-minify-main.php +255 -0
  29. inc/minification/config/default.php +64 -0
  30. inc/minification/config/delayed.php +85 -0
  31. inc/minification/config/minificationFontRegex.php +8 -0
  32. inc/minification/minify/index.html +1 -0
  33. inc/minification/minify/jsmin-1.1.1.php +291 -0
  34. inc/minification/minify/minify-2.1.7-html.php +257 -0
  35. inc/minification/minify/minify-2.1.7-jsmin.php +447 -0
  36. inc/minification/minify/minify-css-compressor.php +250 -0
  37. inc/minification/minify/minify-html.php +268 -0
  38. inc/minification/minify/yui-php-cssmin-2.4.8-4.php +777 -0
  39. inc/minification/minify/yui-php-cssmin-2.4.8-4_fgo.php +789 -0
  40. languages/breeze-en_US.mo +0 -0
  41. languages/breeze-en_US.po +110 -0
  42. languages/breeze-fr_FR.mo +0 -0
  43. languages/breeze-fr_FR.po +87 -0
  44. languages/breeze.pot +111 -0
  45. readme.txt +132 -0
  46. views/breeze-setting-views.php +59 -0
  47. views/tabs/advanced.php +70 -0
  48. views/tabs/basic.php +106 -0
  49. views/tabs/cdn.php +63 -0
  50. views/tabs/database.php +69 -0
  51. views/tabs/varnish.php +43 -0
assets/css/style.css ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .breeze-notice {
2
+ background-color: #FFF;
3
+ padding:10px;
4
+ margin: 15px 0 15px 0;
5
+ border: 1px solid #CCC;
6
+ }
7
+ .breeze-header .breeze-logo{
8
+ background-image: url("../images/hdr-logo.png");
9
+ background-position: center -169px;
10
+ width: 60px;
11
+ height: 60px;
12
+ background-repeat: no-repeat;
13
+ float: left;
14
+ }
15
+ .breeze-header .breeze-logo-title{
16
+ float: left;
17
+ font-size: 16px;
18
+ font-weight:500;
19
+ line-height: 25px;
20
+ margin-top: 20px;
21
+ }
22
+ .breeze-header a{
23
+ text-decoration: none;
24
+ color: #000;
25
+ }
26
+ #breeze-tabs .nav-tab.active {
27
+ background-color: #FFF;
28
+ color: #464646;
29
+ }
30
+
31
+ .tab-child {
32
+ overflow: hidden;
33
+ background: #fff;
34
+ border-bottom: 1px solid #CCC;
35
+ border-left: 1px solid #CCC;
36
+ border-right: 1px solid #CCC;
37
+ padding: 10px 10px 30px 10px
38
+ }
39
+
40
+ #breeze-tabs-content .tab-pane {
41
+ display: none
42
+ }
43
+
44
+ #breeze-tabs-content .tab-pane.active {
45
+ display: block
46
+ }
47
+
48
+ .tab-content {
49
+ overflow: auto;
50
+ }
51
+ .tabs-below > .nav-tabs,
52
+ .tabs-right > .nav-tabs,
53
+ .tabs-left > .nav-tabs {
54
+ border-bottom: 0;
55
+ }
56
+ .tab-content > .tab-pane{
57
+ display: none;
58
+ }
59
+ .tab-content > .active{
60
+ display: block;
61
+ }
62
+
63
+ /*Table*/
64
+ #breeze-tabs-content table tr td:first-child{
65
+ width: 20%;
66
+ font-weight: bold;
67
+ }
68
+ #breeze-tabs-content table tr td.last-td{
69
+ font-weight: normal;
70
+ padding-top: 30px;
71
+ }
72
+ #breeze-tabs-content table tr td{
73
+ vertical-align: top;
74
+ padding: 3px;
75
+ }
76
+
77
+ #breeze-tabs-content table ul{
78
+ margin: 0 ;
79
+ }
80
+ #breeze-tabs-content table tr td label{
81
+ vertical-align: top;
82
+ }
83
+ #breeze-tabs-content table tr td.input-middle{
84
+ vertical-align: middle;
85
+ }
86
+ .breeze-top-notice{
87
+ margin: 10px 0 5px 0;
88
+ }
89
+ .breeze-top-notice label{
90
+ padding-left: 15px;
91
+ font-size: 15px;
92
+ font-weight: 400;
93
+ }
94
+ label.breeze_tool_tip{
95
+ vertical-align: middle !important;
96
+ }
97
+
assets/images/hdr-logo.png ADDED
Binary file
assets/js/breeze-backend.js ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function ($) {
2
+ // Topbar action
3
+ $('#wp-admin-bar-breeze-purge-varnish-group').click(function(){
4
+ breeze_purgeVarnish_callAjax();
5
+ });
6
+ $('#wp-admin-bar-breeze-purge-file-group').click(function(){
7
+ breeze_purgeFile_callAjax();
8
+ });
9
+ $('#wp-admin-bar-breeze-purge-database-group').click(function(){
10
+ breeze_purgeDatabase_callAjax();
11
+ });
12
+ // Varnish clear button
13
+ $('#purge-varnish-button').click(function(){
14
+ breeze_purgeVarnish_callAjax();
15
+ });
16
+
17
+ //clear cache by button
18
+ function breeze_purgeVarnish_callAjax(){
19
+ $.ajax({
20
+ url:ajaxurl,
21
+ dataType:'json',
22
+ method:'POST',
23
+ data:{
24
+ action:'breeze_purge_varnish'
25
+ },
26
+ success : function(res){
27
+ current = location.href;
28
+ if(res.clear){
29
+ var div = '<div id="message" class="notice notice-success" style="margin-top:10px; margin-bottom:10px;padding: 10px;"><strong>OK! Purge Varnish Success</strong></div>';
30
+ //backend
31
+ $("#wpbody .wrap h1").after(div);
32
+ setTimeout(function(){
33
+ location.reload();
34
+ },2000);
35
+ }else{
36
+ window.location.href = current+ "breeze-msg=purge-fail";
37
+ location.reload();
38
+ }
39
+ }
40
+ });
41
+ }
42
+
43
+ function breeze_purgeFile_callAjax(){
44
+ $.ajax({
45
+ url:ajaxurl,
46
+ dataType:'json',
47
+ method:'POST',
48
+ data:{
49
+ action:'breeze_purge_file'
50
+ },
51
+ success : function(res){
52
+ current = location.href;
53
+ res = parseFloat(res) ;
54
+ if(current.indexOf("page=breeze_config") > 0){
55
+ window.location.href = current+ "#breeze-msg=success-cleancache&file="+res;
56
+ }else{
57
+ window.location.href = current+ "breeze-msg=success-cleancache&file="+res;
58
+ }
59
+ location.reload();
60
+ }
61
+ });
62
+ }
63
+
64
+ function breeze_purgeDatabase_callAjax(){
65
+ $.ajax({
66
+ url:ajaxurl,
67
+ dataType:'json',
68
+ method:'POST',
69
+ data:{
70
+ action:'breeze_purge_database'
71
+ },
72
+ success : function(res){
73
+ current = location.href;
74
+ if(res.clear){
75
+ var div = '<div id="message" class="notice notice-success" style="margin-top:10px; margin-bottom:10px;padding: 10px;"><strong>OK! Database cleanup successful</strong></div>';
76
+ //backend
77
+ $("#wpbody .wrap h1").after(div);
78
+ setTimeout(function(){
79
+ location.reload();
80
+ },2000);
81
+ }else{
82
+ window.location.href = current+ "breeze-msg=purge-fail";
83
+ location.reload();
84
+ }
85
+ }
86
+ });
87
+ }
88
+
89
+ function getParameterByName(name, url) {
90
+ if (!url) url = window.location.href;
91
+ name = name.replace(/[\[\]]/g, "\\$&");
92
+ var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
93
+ results = regex.exec(url);
94
+ if (!results) return null;
95
+ if (!results[2]) return '';
96
+ return decodeURIComponent(results[2].replace(/\+/g, " "));
97
+ }
98
+
99
+ var url = location.href;
100
+ var fileClean = parseFloat(getParameterByName('file',url) );
101
+
102
+ $( window ).load(function() {
103
+ var patt = /wp-admin/i;
104
+ if(patt.test(url)){
105
+ //backend
106
+ var div = '';
107
+ if(url.indexOf("msg=success-cleancache") > 0 && !isNaN(fileClean) ) {
108
+ if(fileClean > 0){
109
+ div = '<div id="message" class="notice notice-success" style="margin-top:10px; margin-bottom:10px;padding: 10px;"><strong>OK cache is clean: '+fileClean+'Kb cleaned</strong></div>';
110
+ }else{
111
+ div = '<div id="message" class="notice notice-success" style="margin-top:10px; margin-bottom:10px;padding: 10px;"><strong>OK ! Cache file cleaned successfully</strong></div>';
112
+
113
+ }
114
+
115
+ $("#wpbody .wrap h1").after(div);
116
+
117
+ var url_return = url.split('breeze-msg');
118
+ setTimeout(function(){
119
+ window.location = url_return[0];
120
+ location.reload();
121
+ },2000);
122
+ }
123
+ }else{
124
+ //frontend
125
+ }
126
+
127
+ });
128
+
129
+
130
+ });
assets/js/breeze-configuration.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($){
2
+ // database clean tabs
3
+ $('input[name="all_control"]').click(function () {
4
+ var checked = $(this).is(':checked');
5
+ if (checked == true) {
6
+ $(".clean-data").prop("checked", true);
7
+ } else {
8
+ $(".clean-data").prop("checked", false);
9
+ }
10
+ });
11
+
12
+ $('.clean-data').click(function () {
13
+ var checked = $(this).is(':checked');
14
+ if (checked == false) {
15
+ $('input[name="all_control"]').prop('checked', false);
16
+ }
17
+ });
18
+
19
+ // Tab
20
+ $("#breeze-tabs .nav-tab").click(function (e) {
21
+ e.preventDefault();
22
+ $("#breeze-tabs .nav-tab").removeClass('active');
23
+ $(e.target).addClass('active');
24
+ id_tab = $(this).data('tab-id');
25
+ $("#tab-" + id_tab).addClass('active');
26
+ $("#breeze-tabs-content .tab-pane").removeClass('active');
27
+ $("#tab-content-" + id_tab).addClass('active');
28
+ document.cookie = 'breeze_active_tab=' + id_tab;
29
+ });
30
+
31
+ // Cookie do
32
+ function setTabFromCookie() {
33
+ active_tab = getCookie('breeze_active_tab');
34
+ if (!active_tab){
35
+ active_tab = 'basic';
36
+ }
37
+ $("#tab-" + active_tab).addClass('active');
38
+ $("#tab-content-" + active_tab).addClass('active');
39
+ }
40
+
41
+ function getCookie(cname) {
42
+ var name = cname + "=";
43
+ var ca = document.cookie.split(';');
44
+ for (var i = 0; i < ca.length; i++) {
45
+ var c = ca[i];
46
+ while (c.charAt(0) == ' ') c = c.substring(1);
47
+ if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
48
+ }
49
+ return "";
50
+ }
51
+
52
+ setTabFromCookie();
53
+
54
+ });
breeze.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: Breeze
4
+ * Description: Breeze is a WordPress cache plugin with extensive options to speed up your website. All the options including Varnish Cache are compatible with Cloudways hosting.
5
+ * Version: 1.0.0
6
+ * Text Domain: breeze
7
+ * Domain Path: /languages
8
+ * Author: Cloudways
9
+ * Author URI: https://www.cloudways.com
10
+ * License: GPL2
11
+ */
12
+
13
+ /**
14
+ * @copyright 2017 Cloudways https://www.cloudways.com
15
+ *
16
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
17
+ *
18
+ * This program is free software; you can redistribute it and/or modify
19
+ * it under the terms of the GNU General Public License as published by
20
+ * the Free Software Foundation; either version 2 of the License, or
21
+ * (at your option) any later version.
22
+ *
23
+ * This program is distributed in the hope that it will be useful,
24
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
+ * GNU General Public License for more details.
27
+ *
28
+ * You should have received a copy of the GNU General Public License
29
+ * along with this program; if not, write to the Free Software
30
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31
+ */
32
+
33
+ defined('ABSPATH') || die('No direct script access allowed!');
34
+
35
+ if (!defined('BREEZE_PLUGIN_DIR'))
36
+ define('BREEZE_PLUGIN_DIR', plugin_dir_path(__FILE__));
37
+ if (!defined('BREEZE_VERSION'))
38
+ define('BREEZE_VERSION','1.0.0');
39
+ if (!defined('BREEZE_SITEURL'))
40
+ define('BREEZE_SITEURL', get_site_url());
41
+ if (!defined('BREEZE_MINIFICATION_CACHE'))
42
+ define('BREEZE_MINIFICATION_CACHE', WP_CONTENT_DIR . '/cache/breeze-minification/');
43
+ if (!defined('BREEZE_CACHEFILE_PREFIX'))
44
+ define('BREEZE_CACHEFILE_PREFIX', 'breeze_');
45
+ if (!defined('BREEZE_CACHE_CHILD_DIR'))
46
+ define('BREEZE_CACHE_CHILD_DIR', '/cache/breeze-minification/');
47
+ if (!defined('BREEZE_WP_CONTENT_NAME'))
48
+ define('BREEZE_WP_CONTENT_NAME', '/' . wp_basename(WP_CONTENT_DIR));
49
+ if (!defined('BREEZE_BASENAME'))
50
+ define('BREEZE_BASENAME',plugin_basename(__FILE__));
51
+
52
+ define('BREEZE_CACHE_DELAY', true);
53
+ define('BREEZE_CACHE_NOGZIP', true);
54
+ define('BREEZE_ROOT_DIR', str_replace(BREEZE_WP_CONTENT_NAME, '', WP_CONTENT_DIR));
55
+ //action to purge cache
56
+ require_once(BREEZE_PLUGIN_DIR . 'inc/cache/purge-varnish.php');
57
+ require_once(BREEZE_PLUGIN_DIR . 'inc/cache/purge-cache.php');
58
+ require_once(BREEZE_PLUGIN_DIR . 'inc/cache/purge-per-time.php');
59
+
60
+ // Activate plugin hook
61
+ register_activation_hook(__FILE__,array('Breeze_Admin','plugin_active_hook'));
62
+ //Deactivate plugin hook
63
+ register_deactivation_hook(__FILE__,array('Breeze_Admin','plugin_deactive_hook'));
64
+
65
+
66
+ if(is_admin()){
67
+ require_once(BREEZE_PLUGIN_DIR . 'inc/breeze-admin.php');
68
+ require_once(BREEZE_PLUGIN_DIR . 'inc/breeze-configuration.php');
69
+ //config to cache
70
+ require_once(BREEZE_PLUGIN_DIR . 'inc/cache/config-cache.php');
71
+ }else{
72
+ $cdn_conf = get_option('breeze_cdn_integration');
73
+ $basic_conf = get_option('breeze_basic_settings');
74
+
75
+ if(!empty($cdn_conf['cdn-active']) || !empty($basic_conf['breeze-minify-js']) || !empty($basic_conf['breeze-minify-css']) || !empty($basic_conf['breeze-minify-html'])) {
76
+ // Call back ob start
77
+ ob_start('breeze_ob_start_callback');
78
+ }
79
+ }
80
+
81
+ // Call back ob start - stack
82
+ function breeze_ob_start_callback($buffer){
83
+ $conf = get_option('breeze_cdn_integration');
84
+ // Get buffer from minify
85
+ $buffer = apply_filters('breeze_minify_content_return',$buffer);
86
+
87
+ if(!empty($conf) || !empty($conf['cdn-active'])){
88
+ // Get buffer after remove query strings
89
+ $buffer = apply_filters('breeze_cdn_content_return',$buffer);
90
+ }
91
+ // Return content
92
+ return $buffer;
93
+ }
94
+
95
+ // Minify
96
+ require_once(BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minify-main.php');
97
+ require_once(BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-cache.php');
98
+ new Breeze_Minify();
99
+ // CDN Integration
100
+ if( !class_exists('Breeze_CDN_Integration')){
101
+ require_once ( BREEZE_PLUGIN_DIR. 'inc/cdn-integration/breeze-cdn-integration.php');
102
+ require_once ( BREEZE_PLUGIN_DIR. 'inc/cdn-integration/breeze-cdn-rewrite.php');
103
+ new Breeze_CDN_Integration();
104
+ }
105
+
106
+
inc/breeze-admin.php ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_Admin {
24
+ public function __construct(){
25
+ add_action('init', function(){
26
+ load_plugin_textdomain('breeze', false, dirname(plugin_basename(__FILE__)) . '/languages/');
27
+ });
28
+
29
+ add_action('admin_init', array($this, 'admin_init'));
30
+ //register menu
31
+ add_action('admin_menu', array($this, 'register_menu_page'));
32
+
33
+ $config = get_option('breeze_basic_settings');
34
+
35
+ if(isset($config['breeze-display-clean']) && $config['breeze-display-clean']){
36
+ //register top bar menu
37
+ add_action('admin_bar_menu', array($this, 'register_admin_bar_menu'), 999);
38
+ }
39
+
40
+ /** Load admin js * */
41
+ add_action('admin_enqueue_scripts', array($this, 'loadAdminScripts'));
42
+
43
+ add_action('wp_head', array($this,'define_ajaxurl'));
44
+ $this->ajaxHandle();
45
+
46
+ add_filter( 'plugin_action_links_'.BREEZE_BASENAME, array($this,'breeze_add_action_links') );
47
+ }
48
+
49
+ /**
50
+ * Admin Init
51
+ *
52
+ */
53
+ public function admin_init()
54
+ {
55
+ //Check plugin requirements
56
+ if (version_compare(PHP_VERSION, '5.3', '<')) {
57
+ if (current_user_can('activate_plugins') && is_plugin_active(plugin_basename(__FILE__))) {
58
+ deactivate_plugins(__FILE__);
59
+ add_action('admin_notices', array($this, 'breeze_show_error'));
60
+ unset($_GET['activate']);
61
+ }
62
+ }
63
+ //Do not load anything more
64
+ return;
65
+ }
66
+
67
+ //define ajaxurl
68
+ function define_ajaxurl() {
69
+ if(current_user_can('manage_options')){
70
+ echo '<script type="text/javascript">
71
+ var ajaxurl = "' . admin_url('admin-ajax.php') . '";
72
+ </script>';
73
+ }
74
+ }
75
+
76
+ function loadAdminScripts() {
77
+ wp_enqueue_script('jquery');
78
+ wp_enqueue_script('breeze-backend', plugins_url('assets/js/breeze-backend.js', dirname(__FILE__)), array('jquery'), BREEZE_VERSION, true);
79
+ $current_screen = get_current_screen();
80
+ if($current_screen->base == 'settings_page_breeze'){
81
+ //add css
82
+ wp_enqueue_style('breeze-style', plugins_url('assets/css/style.css', dirname(__FILE__)));
83
+ //js
84
+ wp_enqueue_script('breeze-configuration', plugins_url('assets/js/breeze-configuration.js', dirname(__FILE__)), array('jquery'), BREEZE_VERSION, true);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Register menu
90
+ *
91
+ */
92
+ function register_menu_page()
93
+ {
94
+ //add submenu for cloudsway
95
+ add_submenu_page( 'options-general.php', __('Breeze', 'breeze'), __('Breeze', 'breeze'), 'manage_options', 'breeze', array($this, 'breeze_load_page') );
96
+ }
97
+
98
+
99
+ /**
100
+ * Register bar menu
101
+ *
102
+ */
103
+ function register_admin_bar_menu(WP_Admin_Bar $wp_admin_bar)
104
+ {
105
+ if (current_user_can('manage_options')) {
106
+ // add a parent item
107
+ $args = array(
108
+ 'id' => 'breeze-topbar',
109
+ 'title' => esc_html(__('Breeze', 'breeze')),
110
+ 'meta' => array(
111
+ 'classname' => 'breeze',
112
+ ),
113
+ );
114
+ $wp_admin_bar->add_node( $args );
115
+
116
+ // add purge all item
117
+ $args = array(
118
+ 'id' => 'breeze-purge-all',
119
+ 'title' => esc_html(__('Purge All Cache', 'breeze')),
120
+ 'href' => wp_nonce_url(add_query_arg('breeze_purge', 1), 'breeze_purge_cache'),
121
+ 'parent' => 'breeze-topbar',
122
+ 'meta' => array( 'class' => 'breeze-toolbar-group' ),
123
+ );
124
+ $wp_admin_bar->add_node( $args );
125
+
126
+ // add purge modules group
127
+ $args = array(
128
+ 'id' => 'breeze-purge-modules',
129
+ 'title' => esc_html(__('Purge Modules', 'breeze')),
130
+ 'href' => '#',
131
+ 'parent' => 'breeze-topbar',
132
+ 'meta' => array( 'class' => 'breeze-toolbar-group' ),
133
+ );
134
+ $wp_admin_bar->add_node( $args );
135
+
136
+
137
+ // add child item (Purge Modules)
138
+ $args = array(
139
+ 'id' => 'breeze-purge-varnish-group',
140
+ 'title' => esc_html(__('Purge Varnish', 'breeze')),
141
+ 'href' => '#',
142
+ 'parent' => 'breeze-purge-modules',
143
+ );
144
+ $wp_admin_bar->add_node( $args );
145
+
146
+ // add child item (Purge Modules)
147
+ $args = array(
148
+ 'id' => 'breeze-purge-file-group',
149
+ 'title' => esc_html(__('Purge File', 'breeze')),
150
+ 'href' => '#',
151
+ 'parent' => 'breeze-purge-modules',
152
+ );
153
+ $wp_admin_bar->add_node( $args );
154
+
155
+ // add child item (Purge Modules)
156
+ $args = array(
157
+ 'id' => 'breeze-purge-database-group',
158
+ 'title' => esc_html(__('Purge Database', 'breeze')),
159
+ 'href' => '#',
160
+ 'parent' => 'breeze-purge-modules',
161
+ );
162
+ $wp_admin_bar->add_node( $args );
163
+
164
+ // add settings item
165
+ $args = array(
166
+ 'id' => 'breeze-settings',
167
+ 'title' => esc_html(__('Settings', 'breeze')),
168
+ 'parent' => 'breeze-topbar',
169
+ 'href' => 'options-general.php?page=breeze',
170
+ 'meta' => array( 'class' => 'breeze-toolbar-group' ),
171
+ );
172
+ $wp_admin_bar->add_node( $args );
173
+
174
+ // add support item
175
+ $args = array(
176
+ 'id' => 'breeze-support',
177
+ 'title' => esc_html(__('Support', 'breeze')),
178
+ 'href' => 'https://support.cloudways.com/breeze-wordpress-cache-configuration',
179
+ 'parent' => 'breeze-topbar',
180
+ 'meta' => array( 'class' => 'breeze-toolbar-group',
181
+ 'target' => '_blank'),
182
+ );
183
+ $wp_admin_bar->add_node( $args );
184
+
185
+ }
186
+ }
187
+
188
+ function breeze_load_page()
189
+ {
190
+ if (isset($_GET['page']) && $_GET['page'] == 'breeze') {
191
+ require_once (BREEZE_PLUGIN_DIR . 'views/breeze-setting-views.php');
192
+ }
193
+ }
194
+
195
+ public function breeze_show_error()
196
+ {
197
+ echo '<div class="error"><p><strong>Breeze</strong> need at least PHP 5.3 version, please update php before installing the plugin.</p></div>';
198
+ }
199
+ //ajax admin
200
+ function ajaxHandle() {
201
+ add_action('wp_ajax_breeze_purge_varnish', array('Breeze_Configuration', 'purge_varnish_action'));
202
+ add_action('wp_ajax_breeze_purge_file', array('Breeze_Configuration', 'ajax_clean_cache'));
203
+ add_action('wp_ajax_breeze_purge_database', array('Breeze_Configuration', 'ajax_purge_database'));
204
+ }
205
+ /*
206
+ * Register active plugin hook
207
+ */
208
+ public static function plugin_active_hook(){
209
+ WP_Filesystem();
210
+ // Default basic
211
+ $basic = get_option('breeze_basic_settings');
212
+ if(empty($basic)) $basic = array();
213
+ $default_basic = array(
214
+ 'breeze-active' => '1',
215
+ 'breeze-ttl' => '',
216
+ 'breeze-minify-html' => '0',
217
+ 'breeze-minify-css' => '0',
218
+ 'breeze-minify-js' => '0',
219
+ 'breeze-gzip-compression' => '1',
220
+ 'breeze-desktop-cache' => '1',
221
+ 'breeze-browser-cache' => '1',
222
+ 'breeze-mobile-cache' => '1',
223
+ 'breeze-disable-admin' => '1',
224
+ 'breeze-display-clean' => '1',
225
+ );
226
+ $basic= array_merge($default_basic,$basic);
227
+
228
+ // Default Advanced
229
+ $advanced = get_option('breeze_advanced_settings');
230
+ if(empty($advanced)) $advanced = array();
231
+ $default_advanced = array(
232
+ 'breeze-exclude-urls' => array(),
233
+ 'breeze-group-css' => '0',
234
+ 'breeze-group-js' => '0',
235
+ 'breeze-exclude-css' => array(),
236
+ 'breeze-exclude-js' => array()
237
+ );
238
+ $advanced= array_merge($default_advanced,$advanced);
239
+
240
+ //CDN default
241
+ $cdn = get_option('breeze_cdn_integration');
242
+ if(empty($cdn)) $cdn = array();
243
+ $wp_content = substr(WP_CONTENT_DIR,strlen(ABSPATH));
244
+ $default_cdn = array(
245
+ 'cdn-active' => '0',
246
+ 'cdn-url' =>'',
247
+ 'cdn-content' => array('wp-includes',$wp_content),
248
+ 'cdn-exclude-content' => array('.php'),
249
+ 'cdn-relative-path' =>'1',
250
+ );
251
+ $cdn= array_merge($default_cdn,$cdn);
252
+
253
+ // Varnish default
254
+ $varnish = get_option('breeze_varnish_cache');
255
+ if(empty($varnish)) $varnish = array();
256
+ $default_varnish = array(
257
+ 'auto-purge-varnish' => '1',
258
+ );
259
+ $varnish= array_merge($default_varnish,$varnish);
260
+
261
+ if(is_multisite()){
262
+ $blogs = get_sites();
263
+ foreach ($blogs as $blog){
264
+ update_blog_option((int)$blog->blog_id,'breeze_basic_settings', $basic);
265
+ update_blog_option((int)$blog->blog_id,'breeze_advanced_settings', $advanced);
266
+ update_blog_option((int)$blog->blog_id,'breeze_cdn_integration', $cdn);
267
+ update_blog_option((int)$blog->blog_id,'breeze_varnish_cache', $varnish);
268
+ }
269
+ }else{
270
+ update_option('breeze_basic_settings', $basic);
271
+ update_option('breeze_advanced_settings', $advanced);
272
+ update_option('breeze_cdn_integration', $cdn);
273
+ update_option('breeze_varnish_cache', $varnish);
274
+ }
275
+
276
+ //add header to htaccess by default
277
+ Breeze_Configuration::add_expires_header(TRUE);
278
+ Breeze_Configuration::add_gzip_htacess(TRUE);
279
+ //automatic config start cache
280
+ Breeze_ConfigCache::factory()->write();
281
+ Breeze_ConfigCache::factory()->write_config_cache();
282
+
283
+ if ( !empty($basic) && !empty($basic['breeze-active'] )) {
284
+ Breeze_ConfigCache::factory()->toggle_caching( true );
285
+ }
286
+ }
287
+
288
+ /*
289
+ * Register deactive plugin hook
290
+ */
291
+ public static function plugin_deactive_hook(){
292
+ WP_Filesystem();
293
+ Breeze_ConfigCache::factory()->clean_up();
294
+ Breeze_ConfigCache::factory()->clean_config();
295
+ Breeze_ConfigCache::factory()->toggle_caching(false);
296
+
297
+ }
298
+
299
+ /*
300
+ * Render tab
301
+ */
302
+ public static function render($tab){
303
+ require_once (BREEZE_PLUGIN_DIR . 'views/tabs/'.$tab.'.php');
304
+ }
305
+
306
+ // Check varnish cache exist
307
+ public static function check_varnish(){
308
+ if(isset($_SERVER['HTTP_X_VARNISH'])){
309
+ return true;
310
+ }
311
+ return false;
312
+ }
313
+
314
+ // Applied to the list of links to display on the plugins page
315
+ public function breeze_add_action_links($links){
316
+
317
+ $mylinks = array(
318
+ '<a href="' . admin_url( 'options-general.php?page=breeze' ) . '">Settings</a>',
319
+ );
320
+ return array_merge( $mylinks,$links );
321
+ }
322
+ }
323
+
324
+ $admin = new Breeze_Admin();
inc/breeze-configuration.php ADDED
@@ -0,0 +1,541 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_Configuration{
24
+ public function __construct()
25
+ {
26
+ add_action( 'load-settings_page_breeze', array($this,'afterLoadConfigPage') );
27
+ }
28
+
29
+
30
+ /*
31
+ * function to save settings
32
+ */
33
+ public function afterLoadConfigPage()
34
+ {
35
+ // Basic options tab
36
+ if (isset($_REQUEST['breeze_basic_action']) && $_REQUEST['breeze_basic_action'] == 'breeze_basic_settings') {
37
+ if (isset($_POST['breeze_settings_basic_nonce']) || wp_verify_nonce($_POST['breeze_settings_basic_nonce'], 'breeze_settings_basic')) {
38
+ WP_Filesystem();
39
+
40
+ $basic = array(
41
+ 'breeze-active' =>(isset($_POST['cache-system']) ? '1' : '0'),
42
+ 'breeze-ttl' => (int)$_POST['cache-ttl'],
43
+ 'breeze-minify-html' => (isset($_POST['minification-html']) ? '1' : '0'),
44
+ 'breeze-minify-css' => (isset($_POST['minification-css']) ? '1' : '0'),
45
+ 'breeze-minify-js' => (isset($_POST['minification-js']) ? '1' : '0'),
46
+ 'breeze-gzip-compression' => (isset($_POST['gzip-compression']) ? '1' : '0'),
47
+ 'breeze-browser-cache' => (isset($_POST['browser-cache']) ? '1' : '0'),
48
+ 'breeze-desktop-cache' => (int)$_POST['desktop-cache'],
49
+ 'breeze-mobile-cache' => (int)$_POST['mobile-cache'],
50
+ 'breeze-disable-admin' => '1',
51
+ 'breeze-display-clean' => '1'
52
+ );
53
+ update_option('breeze_basic_settings',$basic);
54
+
55
+ // Storage infomation to cache pages
56
+ Breeze_ConfigCache::factory()->write();
57
+ Breeze_ConfigCache::factory()->write_config_cache();
58
+
59
+ // Turn on WP_CACHE to support advanced-cache file
60
+ if (isset($_POST['cache-system'])) {
61
+ Breeze_ConfigCache::factory()->toggle_caching(true);
62
+ } else {
63
+ Breeze_ConfigCache::factory()->toggle_caching(false);
64
+ }
65
+
66
+ // Reschedule cron events
67
+ if(isset($_POST['cache-system'])){
68
+ Breeze_PurgeCacheTime::factory()->unschedule_events();
69
+ Breeze_PurgeCacheTime::factory()->schedule_events();
70
+ }
71
+ // Add expires header
72
+ if(isset($_POST['gzip-compression'])){
73
+ self::add_gzip_htacess(true);
74
+ }else{
75
+ self::add_gzip_htacess(false);
76
+ }
77
+ // Add expires header
78
+ if(isset($_POST['browser-cache'])){
79
+ self::add_expires_header(true);
80
+ }else{
81
+ self::add_expires_header(false);
82
+ }
83
+
84
+ //delete minify
85
+ Breeze_MinificationCache::clear_minification();
86
+ Breeze_PurgeCache::breeze_cache_flush();
87
+ // Clear varnish cache after settings
88
+ $this->clear_varnish();
89
+ }
90
+ }
91
+ // Advanced options tab
92
+ if (isset($_REQUEST['breeze_advanced_action']) && $_REQUEST['breeze_advanced_action'] == 'breeze_advanced_settings') {
93
+ if (isset($_POST['breeze_settings_advanced_nonce']) || wp_verify_nonce($_POST['breeze_settings_advanced_nonce'], 'breeze_settings_advanced')) {
94
+ $exclude_urls = $this->string_convert_arr(sanitize_textarea_field($_POST['exclude-urls']));
95
+ $exclude_css = $this->string_convert_arr(sanitize_textarea_field($_POST['exclude-css']));
96
+ $exclude_js = $this->string_convert_arr(sanitize_textarea_field($_POST['exclude-js']));
97
+ $advanced = array(
98
+ 'breeze-exclude-urls' => $exclude_urls,
99
+ 'breeze-group-css' => (isset($_POST['group-css']) ? '1' : '0'),
100
+ 'breeze-group-js' => (isset($_POST['group-js']) ? '1' : '0'),
101
+ 'breeze-exclude-css' => $exclude_css,
102
+ 'breeze-exclude-js' => $exclude_js
103
+ );
104
+ update_option('breeze_advanced_settings',$advanced);
105
+
106
+ WP_Filesystem();
107
+ // Storage infomation to cache pages
108
+ Breeze_ConfigCache::factory()->write_config_cache();
109
+
110
+ //delete minify
111
+ Breeze_MinificationCache::clear_minification();
112
+ Breeze_PurgeCache::breeze_cache_flush();
113
+ // Clear varnish cache after settings
114
+ $this->clear_varnish();
115
+ }
116
+ }
117
+
118
+ // Database option tab
119
+ if (isset($_REQUEST['breeze_database_action']) && $_REQUEST['breeze_database_action'] == 'breeze_database_settings') {
120
+ if (isset($_POST['breeze_settings_database_nonce']) || wp_verify_nonce($_POST['breeze_settings_database_nonce'], 'breeze_settings_database')) {
121
+ if(isset($_POST['clean'])){
122
+ foreach ($_POST['clean'] as $item){
123
+ $this->cleanSystem($item);
124
+ }
125
+
126
+ //return current page
127
+ if (!empty($_REQUEST['_wp_http_referer'])) {
128
+ wp_redirect($_REQUEST['_wp_http_referer'].'&database-cleanup=success');
129
+ exit;
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ // Cdn option tab
136
+ if (isset($_REQUEST['breeze_cdn_action']) && $_REQUEST['breeze_cdn_action'] == 'breeze_cdn_settings') {
137
+ if (isset($_POST['breeze_settings_cdn_nonce']) || wp_verify_nonce($_POST['breeze_settings_cdn_nonce'], 'breeze_settings_cdn')) {
138
+ $cdn_content = array();
139
+ $exclude_content = array();
140
+ if(!empty($_POST['cdn-content'])){
141
+ $cdn_content = explode(',',sanitize_text_field($_POST['cdn-content']));
142
+ $cdn_content = array_unique($cdn_content);
143
+ }
144
+ if(!empty($_POST['cdn-exclude-content'])){
145
+ $exclude_content = explode(',',sanitize_text_field($_POST['cdn-exclude-content']));
146
+ $exclude_content = array_unique($exclude_content);
147
+ }
148
+
149
+ $cdn = array(
150
+ 'cdn-active' => (isset($_POST['activate-cdn']) ? '1' : '0'),
151
+ 'cdn-url' =>(isset($_POST['cdn-url']) ? sanitize_text_field($_POST['cdn-url']) : ''),
152
+ 'cdn-content' => $cdn_content,
153
+ 'cdn-exclude-content' => $exclude_content,
154
+ 'cdn-relative-path' =>(isset($_POST['cdn-relative-path']) ? '1' : '0'),
155
+ );
156
+
157
+ update_option('breeze_cdn_integration', $cdn);
158
+
159
+ //delete minify && normal cache
160
+ Breeze_MinificationCache::clear_minification();
161
+ Breeze_PurgeCache::breeze_cache_flush();
162
+ // Clear varnish cache after settings
163
+ $this->clear_varnish();
164
+ }
165
+ }
166
+
167
+ // Varnish option tab
168
+ if (isset($_REQUEST['breeze_varnish_action']) && $_REQUEST['breeze_varnish_action'] == 'breeze_varnish_settings') {
169
+ if (isset($_POST['breeze_settings_varnish_nonce']) || wp_verify_nonce($_POST['breeze_settings_varnish_nonce'], 'breeze_settings_varnish')) {
170
+ $varnish = array(
171
+ 'auto-purge-varnish' => (isset($_POST['auto-purge-varnish']) ? '1' : '0'),
172
+ 'breeze-varnish-server-ip' => preg_replace('/[^a-zA-Z0-9\-\_\.]*/','',$_POST['varnish-server-ip'])
173
+ );
174
+ update_option('breeze_varnish_cache',$varnish);
175
+
176
+ // Clear varnish cache after settings
177
+ $this->clear_varnish();
178
+ }
179
+ }
180
+
181
+
182
+ //return current page
183
+ if (!empty($_REQUEST['_wp_http_referer'])) {
184
+ wp_redirect($_REQUEST['_wp_http_referer'].'&save-settings=success');
185
+ exit;
186
+ }
187
+
188
+ return true;
189
+ }
190
+
191
+ /*
192
+ * function add expires header to .htaccess
193
+ */
194
+ public static function add_expires_header($check) {
195
+ $expires = "#Expires headers configuration added by BREEZE WP CACHE plugin" . PHP_EOL .
196
+ "<IfModule mod_expires.c>" . PHP_EOL .
197
+ " ExpiresActive On" . PHP_EOL .
198
+ " ExpiresDefault A2592000" . PHP_EOL .
199
+ " ExpiresByType application/javascript \"access plus 7 days\"" . PHP_EOL .
200
+ " ExpiresByType text/javascript \"access plus 7 days\"" . PHP_EOL .
201
+ " ExpiresByType text/css \"access plus 7 days\"" . PHP_EOL .
202
+ " ExpiresByType image/jpeg \"access plus 7 days\"" . PHP_EOL .
203
+ " ExpiresByType image/png \"access plus 7 days\"" . PHP_EOL .
204
+ " ExpiresByType image/gif \"access plus 7 days\"" . PHP_EOL .
205
+ " ExpiresByType image/ico \"access plus 7 days\"" . PHP_EOL .
206
+ " ExpiresByType image/x-icon \"access plus 7 days\"" . PHP_EOL .
207
+ " ExpiresByType image/svg+xml \"access plus 7 days\"" . PHP_EOL .
208
+ " ExpiresByType image/bmp \"access plus 7 days\"" . PHP_EOL .
209
+ "</IfModule>" . PHP_EOL .
210
+ "#End of expires headers configuration" . PHP_EOL ;
211
+
212
+ if ($check) {
213
+ if (!is_super_admin()) {
214
+ return FALSE;
215
+ }
216
+ //open htaccess file
217
+ $htaccessContent = file_get_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess');
218
+ if (empty($htaccessContent)) {
219
+ return FALSE;
220
+ }
221
+ //if isset expires header in htacces
222
+ if (strpos($htaccessContent, 'mod_expires') !== false || strpos($htaccessContent, 'ExpiresActive') !== false || strpos($htaccessContent, 'ExpiresDefault') !== false || strpos($htaccessContent, 'ExpiresByType') !== false) {
223
+ return FALSE;
224
+ }
225
+
226
+ $htaccessContent = $expires.$htaccessContent;
227
+ file_put_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess', $htaccessContent);
228
+ return TRUE;
229
+
230
+ } else {
231
+ if (!is_super_admin()) {
232
+ return FALSE;
233
+ }
234
+ //open htaccess file
235
+ $htaccessContent = file_get_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess');
236
+ if (empty($htaccessContent)) {
237
+ return FALSE;
238
+ }
239
+
240
+ $htaccessContent = str_replace($expires,'',$htaccessContent);
241
+ file_put_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess', $htaccessContent);
242
+ return TRUE;
243
+ }
244
+ }
245
+ /*
246
+ * function add gzip header to .htaccess
247
+ */
248
+ public static function add_gzip_htacess($check){
249
+ $data = "# Begin GzipofBreezeWPCache".PHP_EOL.
250
+ "<IfModule mod_deflate.c>".PHP_EOL.
251
+ "AddType x-font/woff .woff".PHP_EOL.
252
+ "AddOutputFilterByType DEFLATE image/svg+xml".PHP_EOL.
253
+ "AddOutputFilterByType DEFLATE text/plain".PHP_EOL.
254
+ "AddOutputFilterByType DEFLATE text/html".PHP_EOL.
255
+ "AddOutputFilterByType DEFLATE text/xml".PHP_EOL.
256
+ "AddOutputFilterByType DEFLATE text/css".PHP_EOL.
257
+ "AddOutputFilterByType DEFLATE text/javascript".PHP_EOL.
258
+ "AddOutputFilterByType DEFLATE application/xml".PHP_EOL.
259
+ "AddOutputFilterByType DEFLATE application/xhtml+xml".PHP_EOL.
260
+ "AddOutputFilterByType DEFLATE application/rss+xml".PHP_EOL.
261
+ "AddOutputFilterByType DEFLATE application/javascript".PHP_EOL.
262
+ "AddOutputFilterByType DEFLATE application/x-javascript".PHP_EOL.
263
+ "AddOutputFilterByType DEFLATE application/x-font-ttf".PHP_EOL.
264
+ "AddOutputFilterByType DEFLATE application/vnd.ms-fontobject".PHP_EOL.
265
+ "AddOutputFilterByType DEFLATE font/opentype font/ttf font/eot font/otf".PHP_EOL.
266
+ "</IfModule>".PHP_EOL.
267
+ "# End GzipofBreezeWPCache" . PHP_EOL ;
268
+ if ($check) {
269
+ if (!is_super_admin()) {
270
+ return FALSE;
271
+ }
272
+ //open htaccess file
273
+ $htaccessContent = file_get_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess');
274
+ if (empty($htaccessContent)) {
275
+ return FALSE;
276
+ }
277
+ //if isset Gzip access
278
+ if (strpos($htaccessContent, 'mod_deflate') !== false || strpos($htaccessContent, 'AddOutputFilterByType') !== false || strpos($htaccessContent, 'AddType') !== false || strpos($htaccessContent,'GzipofBreezeWPCache') !== false) {
279
+ return FALSE;
280
+ }
281
+
282
+ $htaccessContent = $data.$htaccessContent;
283
+ file_put_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess', $htaccessContent);
284
+ return TRUE;
285
+
286
+ } else {
287
+ if (!is_super_admin()) {
288
+ return FALSE;
289
+ }
290
+ //open htaccess file
291
+ $htaccessContent = file_get_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess');
292
+ if (empty($htaccessContent)) {
293
+ return FALSE;
294
+ }
295
+
296
+ $htaccessContent = str_replace($data,'',$htaccessContent);
297
+ file_put_contents(ABSPATH . DIRECTORY_SEPARATOR . '.htaccess', $htaccessContent);
298
+ return TRUE;
299
+ }
300
+ }
301
+ /*
302
+ * Database clean tab
303
+ * funtion to clean in database
304
+ */
305
+ public static function cleanSystem($type){
306
+ global $wpdb;
307
+ $clean = "";
308
+
309
+ switch ($type){
310
+ case "revisions":
311
+ $clean = "DELETE FROM `$wpdb->posts` WHERE post_type = 'revision';";
312
+ $revisions = $wpdb->query( $clean );
313
+
314
+ $message = "All post revisions";
315
+ break;
316
+ case "drafted":
317
+ $clean = "DELETE FROM `$wpdb->posts` WHERE post_status = 'auto-draft';";
318
+ $autodraft = $wpdb->query( $clean );
319
+
320
+ $message = "All auto drafted content";
321
+ break;
322
+ case "trash":
323
+ $clean = "DELETE FROM `$wpdb->posts` WHERE post_status = 'trash';";
324
+ $posttrash = $wpdb->query( $clean );
325
+
326
+ $message = "All trashed content";
327
+ break;
328
+ case "comments":
329
+ $clean = "DELETE FROM `$wpdb->comments` WHERE comment_approved = 'spam' OR comment_approved = 'trash' ;";
330
+ $comments = $wpdb->query( $clean );
331
+
332
+ $message = "Comments from trash & spam";
333
+ break;
334
+ case "trackbacks":
335
+ $clean = "DELETE FROM `$wpdb->comments` WHERE comment_type = 'trackback' OR comment_type = 'pingback' ;";
336
+ $comments = $wpdb->query( $clean );
337
+
338
+ $message = "Trackbacks and pingbacks";
339
+ break;
340
+ case "transient":
341
+ $clean = "DELETE FROM `$wpdb->options` WHERE option_name LIKE '%\_transient\_%' ;";
342
+ $comments = $wpdb->query( $clean );
343
+
344
+ $message = "Transient options";
345
+ break;
346
+ }
347
+
348
+ return true;
349
+ }
350
+
351
+ /*
352
+ * Database clean tab
353
+ * funtion to get number of element to clean in database
354
+ */
355
+ public static function getElementToClean($type){
356
+ global $wpdb;
357
+ $return = 0;
358
+ switch ($type){
359
+ case "revisions":
360
+ $element = "SELECT ID FROM `$wpdb->posts` WHERE post_type = 'revision';";
361
+ $return = $wpdb->query( $element );
362
+ break;
363
+ case "drafted":
364
+ $element = "SELECT ID FROM `$wpdb->posts` WHERE post_status = 'auto-draft';";
365
+ $return = $wpdb->query( $element );
366
+ break;
367
+ case "trash":
368
+ $element = "SELECT ID FROM `$wpdb->posts` WHERE post_status = 'trash';";
369
+ $return = $wpdb->query( $element );
370
+ break;
371
+ case "comments":
372
+ $element = "SELECT comment_ID FROM `$wpdb->comments` WHERE comment_approved = 'spam' OR comment_approved = 'trash' ;";
373
+ $return = $wpdb->query( $element );
374
+ break;
375
+ case "trackbacks":
376
+ $element = "SELECT comment_ID FROM `$wpdb->comments` WHERE comment_type = 'trackback' OR comment_type = 'pingback' ;";
377
+ $return = $wpdb->query( $element );
378
+ break;
379
+ case "transient":
380
+ $element = "SELECT option_id FROM `$wpdb->options` WHERE option_name LIKE '%\_transient\_%' ;";
381
+ $return = $wpdb->query( $element );
382
+ break;
383
+ }
384
+ return $return;
385
+ }
386
+
387
+ // Convert string to array
388
+ protected function string_convert_arr($input){
389
+ $output = array();
390
+ if(!empty($input)){
391
+ $input = rawurldecode($input);
392
+ $input = trim($input);
393
+ $input = str_replace(' ', '', $input);
394
+ $input = explode("\n",$input);
395
+
396
+ foreach ($input as $k => $v){
397
+ $output[] = trim($v);
398
+ }
399
+ }
400
+ return $output;
401
+ }
402
+ //ajax clean cache
403
+ public static function ajax_clean_cache($check = false) {
404
+ $size_cache = 0;
405
+ $size_css_cache = 0;
406
+ $size_js_cache = 0;
407
+ $result = 0;
408
+ // analysis size cache
409
+ $cachepath = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze';
410
+
411
+ if (is_dir($cachepath))
412
+ $cachedirs = scandir($cachepath);
413
+ if (!empty($cachedirs)) {
414
+ foreach ($cachedirs as $cachedir) {
415
+ if ($cachedir != '.' && $cachedir != '..') {
416
+ $filepath = $cachepath . '/' . $cachedir;
417
+ if(is_dir($filepath))
418
+ $filedirs = scandir($filepath);
419
+ foreach($filedirs as $filedir){
420
+ if ($filedir != '.' && $filedir != '..') {
421
+ if (@file_exists($filepath)) {
422
+ $dir_path = $filepath.'/'.$filedir;
423
+ $size_cache += filesize($dir_path);
424
+ }
425
+ }
426
+ }
427
+
428
+ }
429
+ }
430
+ }
431
+
432
+ // analysis size css cache
433
+ if(is_multisite()){
434
+ $blog_id = get_current_blog_id();
435
+ $css_path = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze-minification/'.$blog_id.'/css';
436
+ }else{
437
+ $css_path = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze-minification/css';
438
+ }
439
+ if (is_dir($css_path))
440
+ $file_in_css = scandir($css_path);
441
+ if (!empty($file_in_css)) {
442
+ foreach ($file_in_css as $v) {
443
+ if ($v != '.' && $v != '..' && $v != 'index.html') {
444
+ $path = $css_path . '/' . $v;
445
+ $size_css_cache += filesize($path);
446
+ }
447
+ }
448
+ }
449
+
450
+ // analysis size js cache
451
+ if(is_multisite()){
452
+ $blog_id = get_current_blog_id();
453
+ $js_path = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze-minification/'.$blog_id.'/js';
454
+ }else{
455
+ $js_path = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze-minification/js';
456
+ }
457
+ if (is_dir($js_path))
458
+ ;
459
+ $file_in_js = scandir($js_path);
460
+ if (!empty($file_in_js)) {
461
+ foreach ($file_in_js as $v) {
462
+ if ($v != '.' && $v != '..' && $v != 'index.html') {
463
+ $path = $js_path . '/' . $v;
464
+ $size_js_cache += filesize($path);
465
+ }
466
+ }
467
+ }
468
+
469
+ $total_size_cache = $size_cache + $size_css_cache + $size_js_cache;
470
+
471
+ $result = self::formatBytes($total_size_cache);
472
+
473
+ //delete minify file
474
+ Breeze_MinificationCache::clear_minification();
475
+ //delete all cache
476
+ Breeze_PurgeCache::breeze_cache_flush();
477
+
478
+ if($check){
479
+ return $result;
480
+ }
481
+
482
+ echo json_encode($result);
483
+ exit;
484
+ }
485
+
486
+ public static function purge_varnish_action(){
487
+ $homepage = home_url().'/?breeze';
488
+ $main = new Breeze_PurgeVarnish();
489
+ $main->purge_cache($homepage);
490
+
491
+ echo json_encode(array('clear' => true));
492
+ exit;
493
+ }
494
+
495
+ public static function ajax_purge_database(){
496
+ $type = array('revisions','drafted','trash','comments','trackbacks','transient');
497
+ foreach ($type as $item){
498
+ self::cleanSystem($item);
499
+ }
500
+
501
+ echo json_encode(array('clear' => true));
502
+ exit;
503
+ }
504
+ public static function formatBytes($bytes, $precision = 2) {
505
+ if ($bytes >= 1073741824) {
506
+ $bytes = number_format($bytes / 1073741824, 2);
507
+ } elseif ($bytes >= 1048576) {
508
+ $bytes = number_format($bytes / 1048576, 2);
509
+ } elseif ($bytes >= 1024) {
510
+ $bytes = number_format($bytes / 1024, 2);
511
+ } elseif ($bytes > 1) {
512
+ $bytes = $bytes;
513
+ } elseif ($bytes == 1) {
514
+ $bytes = $bytes;
515
+ } else {
516
+ $bytes = '0';
517
+ }
518
+
519
+ return $bytes;
520
+ }
521
+
522
+ /*
523
+ * Clear varnish after settings
524
+ */
525
+
526
+ public function clear_varnish(){
527
+ // Clear varnish cache after settings
528
+ $varnish = get_option('breeze_varnish_cache');
529
+ if(!empty($varnish['auto-purge-varnish'])){
530
+ $homepage = home_url().'/?breeze';
531
+ $main = new Breeze_PurgeVarnish();
532
+ $main->purge_cache($homepage);
533
+ }
534
+
535
+ return true;
536
+ }
537
+
538
+ }
539
+
540
+ //init configuration object
541
+ new Breeze_Configuration();
inc/cache/Mobile-Detect-2.8.25/LICENSE.txt ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) <2011-2015> Serban Ghita, Nick Ilyin and contributors.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included
14
+ in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ Developer’s Certificate of Origin 1.1
25
+
26
+ By making a contribution to this project, I certify that:
27
+
28
+ (a) The contribution was created in whole or in part by me and I
29
+ have the right to submit it under the open source license
30
+ indicated in the file; or
31
+
32
+ (b) The contribution is based upon previous work that, to the best
33
+ of my knowledge, is covered under an appropriate open source
34
+ license and I have the right under that license to submit that
35
+ work with modifications, whether created in whole or in part
36
+ by me, under the same open source license (unless I am
37
+ permitted to submit under a different license), as indicated
38
+ in the file; or
39
+
40
+ (c) The contribution was provided directly to me by some other
41
+ person who certified (a), (b) or (c) and I have not modified
42
+ it.
43
+
44
+ (d) I understand and agree that this project and the contribution
45
+ are public and that a record of the contribution (including all
46
+ personal information I submit with it, including my sign-off) is
47
+ maintained indefinitely and may be redistributed consistent with
48
+ this project or the open source license(s) involved.
inc/cache/Mobile-Detect-2.8.25/Mobile_Detect.json ADDED
@@ -0,0 +1 @@
 
1
+ {"version":"2.8.25","headerMatch":{"HTTP_ACCEPT":{"matches":["application\/x-obml2d","application\/vnd.rim.html","text\/vnd.wap.wml","application\/vnd.wap.xhtml+xml"]},"HTTP_X_WAP_PROFILE":null,"HTTP_X_WAP_CLIENTID":null,"HTTP_WAP_CONNECTION":null,"HTTP_PROFILE":null,"HTTP_X_OPERAMINI_PHONE_UA":null,"HTTP_X_NOKIA_GATEWAY_ID":null,"HTTP_X_ORANGE_ID":null,"HTTP_X_VODAFONE_3GPDPCONTEXT":null,"HTTP_X_HUAWEI_USERID":null,"HTTP_UA_OS":null,"HTTP_X_MOBILE_GATEWAY":null,"HTTP_X_ATT_DEVICEID":null,"HTTP_UA_CPU":{"matches":["ARM"]}},"uaHttpHeaders":["HTTP_USER_AGENT","HTTP_X_OPERAMINI_PHONE_UA","HTTP_X_DEVICE_USER_AGENT","HTTP_X_ORIGINAL_USER_AGENT","HTTP_X_SKYFIRE_PHONE","HTTP_X_BOLT_PHONE_UA","HTTP_DEVICE_STOCK_UA","HTTP_X_UCBROWSER_DEVICE_UA"],"uaMatch":{"phones":{"iPhone":"\\biPhone\\b|\\biPod\\b","BlackBerry":"BlackBerry|\\bBB10\\b|rim[0-9]+","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","Nexus":"Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6","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","Motorola":"Motorola|DROIDX|DROID BIONIC|\\bDroid\\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\\bMoto E\\b","Samsung":"\\bSamsung\\b|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C","LG":"\\bLG\\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323)","Sony":"SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533","Asus":"Asus.*Galaxy|PadFone.*Mobile","NokiaLumia":"Lumia [0-9]{3,4}","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","Palm":"PalmSource|Palm","Vertu":"Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature","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","Fly":"IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250","Wiko":"KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM","iMobile":"i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)","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","Wolfgang":"AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q","Alcatel":"Alcatel","Nintendo":"Nintendo 3DS","Amoi":"Amoi","INQ":"INQ","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"},"tablets":{"iPad":"iPad|iPad.*Mobile","NexusTablet":"Android.*Nexus[\\s]+(7|9|10)","SamsungTablet":"SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587","Kindle":"Kindle|Silk.*Accelerated|Android.*\\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\\b|Android.*Silk\/[0-9.]+ like Chrome\/[0-9.]+ (?!Mobile)","SurfaceTablet":"Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)","HPTablet":"HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10","AsusTablet":"^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\\bK00F\\b|\\bK00C\\b|\\bK00E\\b|\\bK00L\\b|TX201LA|ME176C|ME102A|\\bM80TA\\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\\bME70C\\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z","BlackBerryTablet":"PlayBook|RIM Tablet","HTCtablet":"HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410","MotorolaTablet":"xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617","NookTablet":"Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2","AcerTablet":"Android.*; \\b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\\b|W3-810|\\bA3-A10\\b|\\bA3-A11\\b|\\bA3-A20\\b|\\bA3-A30","ToshibaTablet":"Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO","LGTablet":"\\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\\b","FujitsuTablet":"Android.*\\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\\b","PrestigioTablet":"PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002","LenovoTablet":"Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)","DellTablet":"Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7","YarvikTablet":"Android.*\\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\\b","MedionTablet":"Android.*\\bOYO\\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB","ArnovaTablet":"97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2","IntensoTablet":"INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004","IRUTablet":"M702pro","MegafonTablet":"MegaFon V9|\\bZTE V9\\b|Android.*\\bMT7A\\b","EbodaTablet":"E-Boda (Supreme|Impresspeed|Izzycomm|Essential)","AllViewTablet":"Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)","ArchosTablet":"\\b(101G9|80G9|A101IT)\\b|Qilive 97R|Archos5|\\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\\b","AinolTablet":"NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark","NokiaLumiaTablet":"Lumia 2520","SonyTablet":"Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP612|SOT31","PhilipsTablet":"\\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\\b","CubeTablet":"Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT","CobyTablet":"MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010","MIDTablet":"M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10","MSITablet":"MSI \\b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\\b","SMiTTablet":"Android.*(\\bMID\\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)","RockChipTablet":"Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A","FlyTablet":"IQ310|Fly Vision","bqTablet":"Android.*(bq)?.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris [E|M]10)|Maxwell.*Lite|Maxwell.*Plus","HuaweiTablet":"MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim","NecTablet":"\\bN-06D|\\bN-08D","PantechTablet":"Pantech.*P4100","BronchoTablet":"Broncho.*(N701|N708|N802|a710)","VersusTablet":"TOUCHPAD.*[78910]|\\bTOUCHTAB\\b","ZyncTablet":"z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900","PositivoTablet":"TB07STA|TB10STA|TB07FTA|TB10FTA","NabiTablet":"Android.*\\bNabi","KoboTablet":"Kobo Touch|\\bK080\\b|\\bVox\\b Build|\\bArc\\b Build","DanewTablet":"DSlide.*\\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\\b","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","PlaystationTablet":"Playstation.*(Portable|Vita)","TrekstorTablet":"ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab","PyleAudioTablet":"\\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\\b","AdvanTablet":"Android.* \\b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\\b ","DanyTechTablet":"Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1","GalapadTablet":"Android.*\\bG1\\b","MicromaxTablet":"Funbook|Micromax.*\\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\\b","KarbonnTablet":"Android.*\\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\\b","AllFineTablet":"Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide","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","YONESTablet":"BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026","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","GUTablet":"TX-A1301|TX-M9002|Q702|kf026","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","OvermaxTablet":"OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)","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","DPSTablet":"DPS Dream 9|DPS Dual 7","VistureTablet":"V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10","CrestaTablet":"CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989","MediatekTablet":"\\bMT8125|MT8389|MT8135|MT8377\\b","ConcordeTablet":"Concorde([ ]+)?Tab|ConCorde ReadMan","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","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","VoninoTablet":"\\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\\bQ8\\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\\b","ECSTablet":"V07OT2|TM105A|S10OT1|TR10CS1","StorexTablet":"eZee[_']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab","VodafoneTablet":"SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497","EssentielBTablet":"Smart[ ']?TAB[ ]+?[0-9]+|Family[ ']?TAB2","RossMoorTablet":"RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711","iMobileTablet":"i-mobile i-note","TolinoTablet":"tolino tab [0-9.]+|tolino shine","AudioSonicTablet":"\\bC-22Q|T7-QC|T-17B|T-17P\\b","AMPETablet":"Android.* A78 ","SkkTablet":"Android.* (SKYPAD|PHOENIX|CYCLOPS)","TecnoTablet":"TECNO P9","JXDTablet":"Android.* \\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\\b","iJoyTablet":"Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)","FX2Tablet":"FX2 PAD7|FX2 PAD10","XoroTablet":"KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151","ViewsonicTablet":"ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a","OdysTablet":"LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\\bXELIO\\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10","CaptivaTablet":"CAPTIVA PAD","IconbitTablet":"NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S","TeclastTablet":"T98 4G|\\bP80\\b|\\bX90HD\\b|X98 Air|X98 Air 3G|\\bX89\\b|P80 3G|\\bX80h\\b|P98 Air|\\bX89HD\\b|P98 3G|\\bP90HD\\b|P89 3G|X98 3G|\\bP70h\\b|P79HD 3G|G18d 3G|\\bP79HD\\b|\\bP89s\\b|\\bA88\\b|\\bP10HD\\b|\\bP19HD\\b|G18 3G|\\bP78HD\\b|\\bA78\\b|\\bP75\\b|G17s 3G|G17h 3G|\\bP85t\\b|\\bP90\\b|\\bP11\\b|\\bP98t\\b|\\bP98HD\\b|\\bG18d\\b|\\bP85s\\b|\\bP11HD\\b|\\bP88s\\b|\\bA80HD\\b|\\bA80se\\b|\\bA10h\\b|\\bP89\\b|\\bP78s\\b|\\bG18\\b|\\bP85\\b|\\bA70h\\b|\\bA70\\b|\\bG17\\b|\\bP18\\b|\\bA80s\\b|\\bA11s\\b|\\bP88HD\\b|\\bA80h\\b|\\bP76s\\b|\\bP76h\\b|\\bP98\\b|\\bA10HD\\b|\\bP78\\b|\\bP88\\b|\\bA11\\b|\\bA10t\\b|\\bP76a\\b|\\bP76t\\b|\\bP76e\\b|\\bP85HD\\b|\\bP85a\\b|\\bP86\\b|\\bP75HD\\b|\\bP76v\\b|\\bA12\\b|\\bP75a\\b|\\bA15\\b|\\bP76Ti\\b|\\bP81HD\\b|\\bA10\\b|\\bT760VE\\b|\\bT720HD\\b|\\bP76\\b|\\bP73\\b|\\bP71\\b|\\bP72\\b|\\bT720SE\\b|\\bC520Ti\\b|\\bT760\\b|\\bT720VE\\b|T720-3GE|T720-WiFi","OndaTablet":"\\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\\b[\\s]+","JaytechTablet":"TPC-PA762","BlaupunktTablet":"Endeavour 800NG|Endeavour 1010","DigmaTablet":"\\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\\b","EvolioTablet":"ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\\bEvotab\\b|\\bNeura\\b","LavaTablet":"QPAD E704|\\bIvoryS\\b|E-TAB IVORY|\\bE-TAB\\b","AocTablet":"MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712","MpmanTablet":"MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\\bMPG7\\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010","CelkonTablet":"CT695|CT888|CT[\\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\\bCT-1\\b","WolderTablet":"miTab \\b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\\b","MiTablet":"\\bMI PAD\\b|\\bHM NOTE 1W\\b","NibiruTablet":"Nibiru M1|Nibiru Jupiter One","NexoTablet":"NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI","LeaderTablet":"TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100","UbislateTablet":"UbiSlate[\\s]?7C","PocketBookTablet":"Pocketbook","KocasoTablet":"\\b(TB-1207)\\b","HisenseTablet":"\\b(F5281|E2371)\\b","Hudl":"Hudl HT7S3|Hudl 2","TelstraTablet":"T-Hub2","GenericTablet":"Android.*\\b97D\\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\\bA7EB\\b|CatNova8|A1_07|CT704|CT1002|\\bM721\\b|rk30sdk|\\bEVOTAB\\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\\bM6pro\\b|CT1020W|arc 10HD|\\bTP750\\b"},"browsers":{"Chrome":"\\bCrMo\\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?","Dolfin":"\\bDolfin\\b","Opera":"Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR\/[0-9.]+|Coast\/[0-9.]+","Skyfire":"Skyfire","Edge":"Mobile Safari\/[.0-9]* Edge","IE":"IEMobile|MSIEMobile","Firefox":"fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS","Bolt":"bolt","TeaShark":"teashark","Blazer":"Blazer","Safari":"Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari","UCBrowser":"UC.*Browser|UCWEB","baiduboxapp":"baiduboxapp","baidubrowser":"baidubrowser","DiigoBrowser":"DiigoBrowser","Puffin":"Puffin","Mercury":"\\bMercury\\b","ObigoBrowser":"Obigo","NetFront":"NF-Browser","GenericBrowser":"NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger","PaleMoon":"Android.*PaleMoon|Mobile.*PaleMoon"},"os":{"AndroidOS":"Android","BlackBerryOS":"blackberry|\\bBB10\\b|rim tablet os","PalmOS":"PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino","SymbianOS":"Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\\bS60\\b","WindowsMobileOS":"Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;","WindowsPhoneOS":"Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;","iOS":"\\biPhone.*Mobile|\\biPod|\\biPad","MeeGoOS":"MeeGo","MaemoOS":"Maemo","JavaOS":"J2ME\/|\\bMIDP\\b|\\bCLDC\\b","webOS":"webOS|hpwOS","badaOS":"\\bBada\\b","BREWOS":"BREW"},"utilities":{"Bot":"Googlebot|facebookexternalhit|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom","MobileBot":"Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker\/M1A1-R2D2","DesktopMode":"WPDesktop","TV":"SonyDTV|HbbTV","WebKit":"(webkit)[ \/]([\\w.]+)","Console":"\\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\\b","Watch":"SM-V700"}}}
inc/cache/Mobile-Detect-2.8.25/Mobile_Detect.php ADDED
@@ -0,0 +1,1460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace Cloudways\Breeze\Mobile_Detect;
3
+ /**
4
+ * Mobile Detect Library
5
+ * =====================
6
+ *
7
+ * Motto: "Every business should have a mobile detection script to detect mobile readers"
8
+ *
9
+ * Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
10
+ * It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.
11
+ *
12
+ * @author Current authors: Serban Ghita <serbanghita@gmail.com>
13
+ * Nick Ilyin <nick.ilyin@gmail.com>
14
+ *
15
+ * Original author: Victor Stanciu <vic.stanciu@gmail.com>
16
+ *
17
+ * @license Code and contributions have 'MIT License'
18
+ * More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
19
+ *
20
+ * @link Homepage: http://mobiledetect.net
21
+ * GitHub Repo: https://github.com/serbanghita/Mobile-Detect
22
+ * Google Code: http://code.google.com/p/php-mobile-detect/
23
+ * README: https://github.com/serbanghita/Mobile-Detect/blob/master/README.md
24
+ * HOWTO: https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples
25
+ *
26
+ * @version 2.8.25
27
+ */
28
+
29
+ class Mobile_Detect
30
+ {
31
+ /**
32
+ * Mobile detection type.
33
+ *
34
+ * @deprecated since version 2.6.9
35
+ */
36
+ const DETECTION_TYPE_MOBILE = 'mobile';
37
+
38
+ /**
39
+ * Extended detection type.
40
+ *
41
+ * @deprecated since version 2.6.9
42
+ */
43
+ const DETECTION_TYPE_EXTENDED = 'extended';
44
+
45
+ /**
46
+ * A frequently used regular expression to extract version #s.
47
+ *
48
+ * @deprecated since version 2.6.9
49
+ */
50
+ const VER = '([\w._\+]+)';
51
+
52
+ /**
53
+ * Top-level device.
54
+ */
55
+ const MOBILE_GRADE_A = 'A';
56
+
57
+ /**
58
+ * Mid-level device.
59
+ */
60
+ const MOBILE_GRADE_B = 'B';
61
+
62
+ /**
63
+ * Low-level device.
64
+ */
65
+ const MOBILE_GRADE_C = 'C';
66
+
67
+ /**
68
+ * Stores the version number of the current release.
69
+ */
70
+ const VERSION = '2.8.25';
71
+
72
+ /**
73
+ * A type for the version() method indicating a string return value.
74
+ */
75
+ const VERSION_TYPE_STRING = 'text';
76
+
77
+ /**
78
+ * A type for the version() method indicating a float return value.
79
+ */
80
+ const VERSION_TYPE_FLOAT = 'float';
81
+
82
+ /**
83
+ * A cache for resolved matches
84
+ * @var array
85
+ */
86
+ protected $cache = array();
87
+
88
+ /**
89
+ * The User-Agent HTTP header is stored in here.
90
+ * @var string
91
+ */
92
+ protected $userAgent = null;
93
+
94
+ /**
95
+ * HTTP headers in the PHP-flavor. So HTTP_USER_AGENT and SERVER_SOFTWARE.
96
+ * @var array
97
+ */
98
+ protected $httpHeaders = array();
99
+
100
+ /**
101
+ * CloudFront headers. E.g. CloudFront-Is-Desktop-Viewer, CloudFront-Is-Mobile-Viewer & CloudFront-Is-Tablet-Viewer.
102
+ * @var array
103
+ */
104
+ protected $cloudfrontHeaders = array();
105
+
106
+ /**
107
+ * The matching Regex.
108
+ * This is good for debug.
109
+ * @var string
110
+ */
111
+ protected $matchingRegex = null;
112
+
113
+ /**
114
+ * The matches extracted from the regex expression.
115
+ * This is good for debug.
116
+ * @var string
117
+ */
118
+ protected $matchesArray = null;
119
+
120
+ /**
121
+ * The detection type, using self::DETECTION_TYPE_MOBILE or self::DETECTION_TYPE_EXTENDED.
122
+ *
123
+ * @deprecated since version 2.6.9
124
+ *
125
+ * @var string
126
+ */
127
+ protected $detectionType = self::DETECTION_TYPE_MOBILE;
128
+
129
+ /**
130
+ * HTTP headers that trigger the 'isMobile' detection
131
+ * to be true.
132
+ *
133
+ * @var array
134
+ */
135
+ protected static $mobileHeaders = array(
136
+
137
+ 'HTTP_ACCEPT' => array('matches' => array(
138
+ // Opera Mini; @reference: http://dev.opera.com/articles/view/opera-binary-markup-language/
139
+ 'application/x-obml2d',
140
+ // BlackBerry devices.
141
+ 'application/vnd.rim.html',
142
+ 'text/vnd.wap.wml',
143
+ 'application/vnd.wap.xhtml+xml'
144
+ )),
145
+ 'HTTP_X_WAP_PROFILE' => null,
146
+ 'HTTP_X_WAP_CLIENTID' => null,
147
+ 'HTTP_WAP_CONNECTION' => null,
148
+ 'HTTP_PROFILE' => null,
149
+ // Reported by Opera on Nokia devices (eg. C3).
150
+ 'HTTP_X_OPERAMINI_PHONE_UA' => null,
151
+ 'HTTP_X_NOKIA_GATEWAY_ID' => null,
152
+ 'HTTP_X_ORANGE_ID' => null,
153
+ 'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
154
+ 'HTTP_X_HUAWEI_USERID' => null,
155
+ // Reported by Windows Smartphones.
156
+ 'HTTP_UA_OS' => null,
157
+ // Reported by Verizon, Vodafone proxy system.
158
+ 'HTTP_X_MOBILE_GATEWAY' => null,
159
+ // Seen this on HTC Sensation. SensationXE_Beats_Z715e.
160
+ 'HTTP_X_ATT_DEVICEID' => null,
161
+ // Seen this on a HTC.
162
+ 'HTTP_UA_CPU' => array('matches' => array('ARM')),
163
+ );
164
+
165
+ /**
166
+ * List of mobile devices (phones).
167
+ *
168
+ * @var array
169
+ */
170
+ protected static $phoneDevices = array(
171
+ 'iPhone' => '\biPhone\b|\biPod\b', // |\biTunes
172
+ 'BlackBerry' => 'BlackBerry|\bBB10\b|rim[0-9]+',
173
+ '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',
174
+ 'Nexus' => 'Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 6',
175
+ // @todo: Is 'Dell Streak' a tablet or a phone? ;)
176
+ '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',
177
+ 'Motorola' => 'Motorola|DROIDX|DROID BIONIC|\bDroid\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\bMoto E\b',
178
+ 'Samsung' => '\bSamsung\b|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C',
179
+ 'LG' => '\bLG\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323)',
180
+ 'Sony' => 'SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533',
181
+ 'Asus' => 'Asus.*Galaxy|PadFone.*Mobile',
182
+ 'NokiaLumia' => 'Lumia [0-9]{3,4}',
183
+ // http://www.micromaxinfo.com/mobiles/smartphones
184
+ // Added because the codes might conflict with Acer Tablets.
185
+ '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',
186
+ // @todo Complete the regex.
187
+ 'Palm' => 'PalmSource|Palm', // avantgo|blazer|elaine|hiptop|plucker|xiino ;
188
+ 'Vertu' => 'Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature', // Just for fun ;)
189
+ // http://www.pantech.co.kr/en/prod/prodList.do?gbrand=VEGA (PANTECH)
190
+ // Most of the VEGA devices are legacy. PANTECH seem to be newer devices based on Android.
191
+ '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',
192
+ // http://www.fly-phone.com/devices/smartphones/ ; Included only smartphones.
193
+ 'Fly' => 'IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250',
194
+ // http://fr.wikomobile.com
195
+ 'Wiko' => 'KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM',
196
+ 'iMobile' => 'i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)',
197
+ // Added simvalley mobile just for fun. They have some interesting devices.
198
+ // http://www.simvalley.fr/telephonie---gps-_22_telephonie-mobile_telephones_.html
199
+ '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',
200
+ // Wolfgang - a brand that is sold by Aldi supermarkets.
201
+ // http://www.wolfgangmobile.com/
202
+ 'Wolfgang' => 'AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q',
203
+ 'Alcatel' => 'Alcatel',
204
+ 'Nintendo' => 'Nintendo 3DS',
205
+ // http://en.wikipedia.org/wiki/Amoi
206
+ 'Amoi' => 'Amoi',
207
+ // http://en.wikipedia.org/wiki/INQ
208
+ 'INQ' => 'INQ',
209
+ // @Tapatalk is a mobile app; http://support.tapatalk.com/threads/smf-2-0-2-os-and-browser-detection-plugin-and-tapatalk.15565/#post-79039
210
+ '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',
211
+ );
212
+
213
+ /**
214
+ * List of tablet devices.
215
+ *
216
+ * @var array
217
+ */
218
+ protected static $tabletDevices = array(
219
+ // @todo: check for mobile friendly emails topic.
220
+ 'iPad' => 'iPad|iPad.*Mobile',
221
+ // Removed |^.*Android.*Nexus(?!(?:Mobile).)*$
222
+ // @see #442
223
+ 'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
224
+ 'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587', // SCH-P709|SCH-P729|SM-T2558|GT-I9205 - Samsung Mega - treat them like a regular phone.
225
+ // http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
226
+ 'Kindle' => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
227
+ // Only the Surface tablets with Windows RT are considered mobile.
228
+ // http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
229
+ 'SurfaceTablet' => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
230
+ // http://shopping1.hp.com/is-bin/INTERSHOP.enfinity/WFS/WW-USSMBPublicStore-Site/en_US/-/USD/ViewStandardCatalog-Browse?CatalogCategoryID=JfIQ7EN5lqMAAAEyDcJUDwMT
231
+ 'HPTablet' => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
232
+ // Watch out for PadFone, see #132.
233
+ // http://www.asus.com/de/Tablets_Mobile/Memo_Pad_Products/
234
+ 'AsusTablet' => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z',
235
+ 'BlackBerryTablet' => 'PlayBook|RIM Tablet',
236
+ 'HTCtablet' => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
237
+ 'MotorolaTablet' => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
238
+ 'NookTablet' => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
239
+ // http://www.acer.ro/ac/ro/RO/content/drivers
240
+ // http://www.packardbell.co.uk/pb/en/GB/content/download (Packard Bell is part of Acer)
241
+ // http://us.acer.com/ac/en/US/content/group/tablets
242
+ // http://www.acer.de/ac/de/DE/content/models/tablets/
243
+ // Can conflict with Micromax and Motorola phones codes.
244
+ 'AcerTablet' => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30',
245
+ // http://eu.computers.toshiba-europe.com/innovation/family/Tablets/1098744/banner_id/tablet_footerlink/
246
+ // http://us.toshiba.com/tablets/tablet-finder
247
+ // http://www.toshiba.co.jp/regza/tablet/
248
+ 'ToshibaTablet' => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
249
+ // http://www.nttdocomo.co.jp/english/service/developer/smart_phone/technical_info/spec/index.html
250
+ // http://www.lg.com/us/tablets
251
+ 'LGTablet' => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
252
+ 'FujitsuTablet' => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
253
+ // Prestigio Tablets http://www.prestigio.com/support
254
+ 'PrestigioTablet' => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
255
+ // http://support.lenovo.com/en_GB/downloads/default.page?#
256
+ 'LenovoTablet' => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)',
257
+ // http://www.dell.com/support/home/us/en/04/Products/tab_mob/tablets
258
+ 'DellTablet' => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
259
+ // http://www.yarvik.com/en/matrix/tablets/
260
+ 'YarvikTablet' => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
261
+ 'MedionTablet' => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
262
+ 'ArnovaTablet' => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
263
+ // http://www.intenso.de/kategorie_en.php?kategorie=33
264
+ // @todo: http://www.nbhkdz.com/read/b8e64202f92a2df129126bff.html - investigate
265
+ 'IntensoTablet' => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
266
+ // IRU.ru Tablets http://www.iru.ru/catalog/soho/planetable/
267
+ 'IRUTablet' => 'M702pro',
268
+ 'MegafonTablet' => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
269
+ // http://www.e-boda.ro/tablete-pc.html
270
+ 'EbodaTablet' => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
271
+ // http://www.allview.ro/produse/droseries/lista-tablete-pc/
272
+ 'AllViewTablet' => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
273
+ // http://wiki.archosfans.com/index.php?title=Main_Page
274
+ // @note Rewrite the regex format after we add more UAs.
275
+ 'ArchosTablet' => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
276
+ // http://www.ainol.com/plugin.php?identifier=ainol&module=product
277
+ 'AinolTablet' => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
278
+ 'NokiaLumiaTablet' => 'Lumia 2520',
279
+ // @todo: inspect http://esupport.sony.com/US/p/select-system.pl?DIRECTOR=DRIVER
280
+ // Readers http://www.atsuhiro-me.net/ebook/sony-reader/sony-reader-web-browser
281
+ // http://www.sony.jp/support/tablet/
282
+ 'SonyTablet' => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP612|SOT31',
283
+ // http://www.support.philips.com/support/catalog/worldproducts.jsp?userLanguage=en&userCountry=cn&categoryid=3G_LTE_TABLET_SU_CN_CARE&title=3G%20tablets%20/%20LTE%20range&_dyncharset=UTF-8
284
+ 'PhilipsTablet' => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
285
+ // db + http://www.cube-tablet.com/buy-products.html
286
+ 'CubeTablet' => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
287
+ // http://www.cobyusa.com/?p=pcat&pcat_id=3001
288
+ 'CobyTablet' => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
289
+ // http://www.match.net.cn/products.asp
290
+ 'MIDTablet' => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
291
+ // http://www.msi.com/support
292
+ // @todo Research the Windows Tablets.
293
+ 'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
294
+ // @todo http://www.kyoceramobile.com/support/drivers/
295
+ // 'KyoceraTablet' => null,
296
+ // @todo http://intexuae.com/index.php/category/mobile-devices/tablets-products/
297
+ // 'IntextTablet' => null,
298
+ // http://pdadb.net/index.php?m=pdalist&list=SMiT (NoName Chinese Tablets)
299
+ // http://www.imp3.net/14/show.php?itemid=20454
300
+ 'SMiTTablet' => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
301
+ // http://www.rock-chips.com/index.php?do=prod&pid=2
302
+ 'RockChipTablet' => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
303
+ // http://www.fly-phone.com/devices/tablets/ ; http://www.fly-phone.com/service/
304
+ 'FlyTablet' => 'IQ310|Fly Vision',
305
+ // http://www.bqreaders.com/gb/tablets-prices-sale.html
306
+ 'bqTablet' => 'Android.*(bq)?.*(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris [E|M]10)|Maxwell.*Lite|Maxwell.*Plus',
307
+ // http://www.huaweidevice.com/worldwide/productFamily.do?method=index&directoryId=5011&treeId=3290
308
+ // http://www.huaweidevice.com/worldwide/downloadCenter.do?method=index&directoryId=3372&treeId=0&tb=1&type=software (including legacy tablets)
309
+ 'HuaweiTablet' => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim',
310
+ // Nec or Medias Tab
311
+ 'NecTablet' => '\bN-06D|\bN-08D',
312
+ // Pantech Tablets: http://www.pantechusa.com/phones/
313
+ 'PantechTablet' => 'Pantech.*P4100',
314
+ // Broncho Tablets: http://www.broncho.cn/ (hard to find)
315
+ 'BronchoTablet' => 'Broncho.*(N701|N708|N802|a710)',
316
+ // http://versusuk.com/support.html
317
+ 'VersusTablet' => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
318
+ // http://www.zync.in/index.php/our-products/tablet-phablets
319
+ 'ZyncTablet' => 'z1000|Z99 2G|z99|z930|z999|z990|z909|Z919|z900',
320
+ // http://www.positivoinformatica.com.br/www/pessoal/tablet-ypy/
321
+ 'PositivoTablet' => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
322
+ // https://www.nabitablet.com/
323
+ 'NabiTablet' => 'Android.*\bNabi',
324
+ 'KoboTablet' => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
325
+ // French Danew Tablets http://www.danew.com/produits-tablette.php
326
+ 'DanewTablet' => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
327
+ // Texet Tablets and Readers http://www.texet.ru/tablet/
328
+ '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',
329
+ // Avoid detecting 'PLAYSTATION 3' as mobile.
330
+ 'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
331
+ // http://www.trekstor.de/surftabs.html
332
+ 'TrekstorTablet' => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
333
+ // http://www.pyleaudio.com/Products.aspx?%2fproducts%2fPersonal-Electronics%2fTablets
334
+ 'PyleAudioTablet' => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
335
+ // http://www.advandigital.com/index.php?link=content-product&jns=JP001
336
+ // because of the short codenames we have to include whitespaces to reduce the possible conflicts.
337
+ 'AdvanTablet' => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
338
+ // http://www.danytech.com/category/tablet-pc
339
+ 'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
340
+ // http://www.galapad.net/product.html
341
+ 'GalapadTablet' => 'Android.*\bG1\b',
342
+ // http://www.micromaxinfo.com/tablet/funbook
343
+ 'MicromaxTablet' => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
344
+ // http://www.karbonnmobiles.com/products_tablet.php
345
+ 'KarbonnTablet' => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
346
+ // http://www.myallfine.com/Products.asp
347
+ 'AllFineTablet' => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
348
+ // http://www.proscanvideo.com/products-search.asp?itemClass=TABLET&itemnmbr=
349
+ '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',
350
+ // http://www.yonesnav.com/products/products.php
351
+ 'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
352
+ // http://www.cjshowroom.com/eproducts.aspx?classcode=004001001
353
+ // China manufacturer makes tablets for different small brands (eg. http://www.zeepad.net/index.html)
354
+ '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',
355
+ // http://www.gloryunion.cn/products.asp
356
+ // http://www.allwinnertech.com/en/apply/mobile.html
357
+ // http://www.ptcl.com.pk/pd_content.php?pd_id=284 (EVOTAB)
358
+ // @todo: Softwiner tablets?
359
+ // aka. Cute or Cool tablets. Not sure yet, must research to avoid collisions.
360
+ 'GUTablet' => 'TX-A1301|TX-M9002|Q702|kf026', // A12R|D75A|D77|D79|R83|A95|A106C|R15|A75|A76|D71|D72|R71|R73|R77|D82|R85|D92|A97|D92|R91|A10F|A77F|W71F|A78F|W78F|W81F|A97F|W91F|W97F|R16G|C72|C73E|K72|K73|R96G
361
+ // http://www.pointofview-online.com/showroom.php?shop_mode=product_listing&category_id=118
362
+ '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',
363
+ // http://www.overmax.pl/pl/katalog-produktow,p8/tablety,c14/
364
+ // @todo: add more tests.
365
+ 'OvermaxTablet' => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)',
366
+ // http://hclmetablet.com/India/index.php
367
+ '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',
368
+ // http://www.edigital.hu/Tablet_es_e-book_olvaso/Tablet-c18385.html
369
+ 'DPSTablet' => 'DPS Dream 9|DPS Dual 7',
370
+ // http://www.visture.com/index.asp
371
+ 'VistureTablet' => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
372
+ // http://www.mijncresta.nl/tablet
373
+ 'CrestaTablet' => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
374
+ // MediaTek - http://www.mediatek.com/_en/01_products/02_proSys.php?cata_sn=1&cata1_sn=1&cata2_sn=309
375
+ 'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
376
+ // Concorde tab
377
+ 'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
378
+ // GoClever Tablets - http://www.goclever.com/uk/products,c1/tablet,c5/
379
+ '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',
380
+ // Modecom Tablets - http://www.modecom.eu/tablets/portal/
381
+ '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',
382
+ // Vonino Tablets - http://www.vonino.eu/tablets
383
+ 'VoninoTablet' => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
384
+ // ECS Tablets - http://www.ecs.com.tw/ECSWebSite/Product/Product_Tablet_List.aspx?CategoryID=14&MenuID=107&childid=M_107&LanID=0
385
+ 'ECSTablet' => 'V07OT2|TM105A|S10OT1|TR10CS1',
386
+ // Storex Tablets - http://storex.fr/espace_client/support.html
387
+ // @note: no need to add all the tablet codes since they are guided by the first regex.
388
+ 'StorexTablet' => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
389
+ // Generic Vodafone tablets.
390
+ 'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497',
391
+ // French tablets - Essentiel B http://www.boulanger.fr/tablette_tactile_e-book/tablette_tactile_essentiel_b/cl_68908.htm?multiChoiceToDelete=brand&mc_brand=essentielb
392
+ // Aka: http://www.essentielb.fr/
393
+ 'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
394
+ // Ross & Moor - http://ross-moor.ru/
395
+ 'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
396
+ // i-mobile http://product.i-mobilephone.com/Mobile_Device
397
+ 'iMobileTablet' => 'i-mobile i-note',
398
+ // http://www.tolino.de/de/vergleichen/
399
+ 'TolinoTablet' => 'tolino tab [0-9.]+|tolino shine',
400
+ // AudioSonic - a Kmart brand
401
+ // http://www.kmart.com.au/webapp/wcs/stores/servlet/Search?langId=-1&storeId=10701&catalogId=10001&categoryId=193001&pageSize=72&currentPage=1&searchCategory=193001%2b4294965664&sortBy=p_MaxPrice%7c1
402
+ 'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
403
+ // AMPE Tablets - http://www.ampe.com.my/product-category/tablets/
404
+ // @todo: add them gradually to avoid conflicts.
405
+ 'AMPETablet' => 'Android.* A78 ',
406
+ // Skk Mobile - http://skkmobile.com.ph/product_tablets.php
407
+ 'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
408
+ // Tecno Mobile (only tablet) - http://www.tecno-mobile.com/index.php/product?filterby=smart&list_order=all&page=1
409
+ 'TecnoTablet' => 'TECNO P9',
410
+ // JXD (consoles & tablets) - http://jxd.hk/products.asp?selectclassid=009008&clsid=3
411
+ 'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
412
+ // i-Joy tablets - http://www.i-joy.es/en/cat/products/tablets/
413
+ 'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
414
+ // http://www.intracon.eu/tablet
415
+ 'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
416
+ // http://www.xoro.de/produkte/
417
+ // @note: Might be the same brand with 'Simply tablets'
418
+ 'XoroTablet' => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
419
+ // http://www1.viewsonic.com/products/computing/tablets/
420
+ 'ViewsonicTablet' => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
421
+ // http://www.odys.de/web/internet-tablet_en.html
422
+ 'OdysTablet' => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
423
+ // http://www.captiva-power.de/products.html#tablets-en
424
+ 'CaptivaTablet' => 'CAPTIVA PAD',
425
+ // IconBIT - http://www.iconbit.com/products/tablets/
426
+ 'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
427
+ // http://www.teclast.com/topic.php?channelID=70&topicID=140&pid=63
428
+ 'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
429
+ // Onda - http://www.onda-tablet.com/buy-android-onda.html?dir=desc&limit=all&order=price
430
+ 'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+',
431
+ 'JaytechTablet' => 'TPC-PA762',
432
+ 'BlaupunktTablet' => 'Endeavour 800NG|Endeavour 1010',
433
+ // http://www.digma.ru/support/download/
434
+ // @todo: Ebooks also (if requested)
435
+ 'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
436
+ // http://www.evolioshop.com/ro/tablete-pc.html
437
+ // http://www.evolio.ro/support/downloads_static.html?cat=2
438
+ // @todo: Research some more
439
+ 'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
440
+ // @todo http://www.lavamobiles.com/tablets-data-cards
441
+ 'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
442
+ // http://www.breezetablet.com/
443
+ 'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
444
+ // http://www.mpmaneurope.com/en/products/internet-tablets-14/android-tablets-14/
445
+ 'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
446
+ // https://www.celkonmobiles.com/?_a=categoryphones&sid=2
447
+ 'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
448
+ // http://www.wolderelectronics.com/productos/manuales-y-guias-rapidas/categoria-2-miTab
449
+ 'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
450
+ // http://www.mi.com/en
451
+ 'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
452
+ // http://www.nbru.cn/index.html
453
+ 'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
454
+ // http://navroad.com/products/produkty/tablety/
455
+ 'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
456
+ // http://leader-online.com/new_site/product-category/tablets/
457
+ // http://www.leader-online.net.au/List/Tablet
458
+ 'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
459
+ // http://www.datawind.com/ubislate/
460
+ 'UbislateTablet' => 'UbiSlate[\s]?7C',
461
+ // http://www.pocketbook-int.com/ru/support
462
+ 'PocketBookTablet' => 'Pocketbook',
463
+ // http://www.kocaso.com/product_tablet.html
464
+ 'KocasoTablet' => '\b(TB-1207)\b',
465
+ // http://global.hisense.com/product/asia/tablet/Sero7/201412/t20141215_91832.htm
466
+ 'HisenseTablet' => '\b(F5281|E2371)\b',
467
+ // http://www.tesco.com/direct/hudl/
468
+ 'Hudl' => 'Hudl HT7S3|Hudl 2',
469
+ // http://www.telstra.com.au/home-phone/thub-2/
470
+ 'TelstraTablet' => 'T-Hub2',
471
+ 'GenericTablet' => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b'
472
+ );
473
+
474
+ /**
475
+ * List of mobile Operating Systems.
476
+ *
477
+ * @var array
478
+ */
479
+ protected static $operatingSystems = array(
480
+ 'AndroidOS' => 'Android',
481
+ 'BlackBerryOS' => 'blackberry|\bBB10\b|rim tablet os',
482
+ 'PalmOS' => 'PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino',
483
+ 'SymbianOS' => 'Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\bS60\b',
484
+ // @reference: http://en.wikipedia.org/wiki/Windows_Mobile
485
+ 'WindowsMobileOS' => 'Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Window Mobile|Windows Phone [0-9.]+|WCE;',
486
+ // @reference: http://en.wikipedia.org/wiki/Windows_Phone
487
+ // http://wifeng.cn/?r=blog&a=view&id=106
488
+ // http://nicksnettravels.builttoroam.com/post/2011/01/10/Bogus-Windows-Phone-7-User-Agent-String.aspx
489
+ // http://msdn.microsoft.com/library/ms537503.aspx
490
+ // https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
491
+ 'WindowsPhoneOS' => 'Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;',
492
+ 'iOS' => '\biPhone.*Mobile|\biPod|\biPad',
493
+ // http://en.wikipedia.org/wiki/MeeGo
494
+ // @todo: research MeeGo in UAs
495
+ 'MeeGoOS' => 'MeeGo',
496
+ // http://en.wikipedia.org/wiki/Maemo
497
+ // @todo: research Maemo in UAs
498
+ 'MaemoOS' => 'Maemo',
499
+ 'JavaOS' => 'J2ME/|\bMIDP\b|\bCLDC\b', // '|Java/' produces bug #135
500
+ 'webOS' => 'webOS|hpwOS',
501
+ 'badaOS' => '\bBada\b',
502
+ 'BREWOS' => 'BREW',
503
+ );
504
+
505
+ /**
506
+ * List of mobile User Agents.
507
+ *
508
+ * IMPORTANT: This is a list of only mobile browsers.
509
+ * Mobile Detect 2.x supports only mobile browsers,
510
+ * it was never designed to detect all browsers.
511
+ * The change will come in 2017 in the 3.x release for PHP7.
512
+ *
513
+ * @var array
514
+ */
515
+ protected static $browsers = array(
516
+ //'Vivaldi' => 'Vivaldi',
517
+ // @reference: https://developers.google.com/chrome/mobile/docs/user-agent
518
+ 'Chrome' => '\bCrMo\b|CriOS|Android.*Chrome/[.0-9]* (Mobile)?',
519
+ 'Dolfin' => '\bDolfin\b',
520
+ 'Opera' => 'Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR/[0-9.]+|Coast/[0-9.]+',
521
+ 'Skyfire' => 'Skyfire',
522
+ 'Edge' => 'Mobile Safari/[.0-9]* Edge',
523
+ 'IE' => 'IEMobile|MSIEMobile', // |Trident/[.0-9]+
524
+ 'Firefox' => 'fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS',
525
+ 'Bolt' => 'bolt',
526
+ 'TeaShark' => 'teashark',
527
+ 'Blazer' => 'Blazer',
528
+ // @reference: http://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/OptimizingforSafarioniPhone/OptimizingforSafarioniPhone.html#//apple_ref/doc/uid/TP40006517-SW3
529
+ 'Safari' => 'Version.*Mobile.*Safari|Safari.*Mobile|MobileSafari',
530
+ // http://en.wikipedia.org/wiki/Midori_(web_browser)
531
+ //'Midori' => 'midori',
532
+ //'Tizen' => 'Tizen',
533
+ 'UCBrowser' => 'UC.*Browser|UCWEB',
534
+ 'baiduboxapp' => 'baiduboxapp',
535
+ 'baidubrowser' => 'baidubrowser',
536
+ // https://github.com/serbanghita/Mobile-Detect/issues/7
537
+ 'DiigoBrowser' => 'DiigoBrowser',
538
+ // http://www.puffinbrowser.com/index.php
539
+ 'Puffin' => 'Puffin',
540
+ // http://mercury-browser.com/index.html
541
+ 'Mercury' => '\bMercury\b',
542
+ // http://en.wikipedia.org/wiki/Obigo_Browser
543
+ 'ObigoBrowser' => 'Obigo',
544
+ // http://en.wikipedia.org/wiki/NetFront
545
+ 'NetFront' => 'NF-Browser',
546
+ // @reference: http://en.wikipedia.org/wiki/Minimo
547
+ // http://en.wikipedia.org/wiki/Vision_Mobile_Browser
548
+ 'GenericBrowser' => 'NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger',
549
+ // @reference: https://en.wikipedia.org/wiki/Pale_Moon_(web_browser)
550
+ 'PaleMoon' => 'Android.*PaleMoon|Mobile.*PaleMoon',
551
+ );
552
+
553
+ /**
554
+ * Utilities.
555
+ *
556
+ * @var array
557
+ */
558
+ protected static $utilities = array(
559
+ // Experimental. When a mobile device wants to switch to 'Desktop Mode'.
560
+ // http://scottcate.com/technology/windows-phone-8-ie10-desktop-or-mobile/
561
+ // https://github.com/serbanghita/Mobile-Detect/issues/57#issuecomment-15024011
562
+ // https://developers.facebook.com/docs/sharing/best-practices
563
+ 'Bot' => 'Googlebot|facebookexternalhit|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom',
564
+ 'MobileBot' => 'Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker/M1A1-R2D2',
565
+ 'DesktopMode' => 'WPDesktop',
566
+ 'TV' => 'SonyDTV|HbbTV', // experimental
567
+ 'WebKit' => '(webkit)[ /]([\w.]+)',
568
+ // @todo: Include JXD consoles.
569
+ 'Console' => '\b(Nintendo|Nintendo WiiU|Nintendo 3DS|PLAYSTATION|Xbox)\b',
570
+ 'Watch' => 'SM-V700',
571
+ );
572
+
573
+ /**
574
+ * All possible HTTP headers that represent the
575
+ * User-Agent string.
576
+ *
577
+ * @var array
578
+ */
579
+ protected static $uaHttpHeaders = array(
580
+ // The default User-Agent string.
581
+ 'HTTP_USER_AGENT',
582
+ // Header can occur on devices using Opera Mini.
583
+ 'HTTP_X_OPERAMINI_PHONE_UA',
584
+ // Vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
585
+ 'HTTP_X_DEVICE_USER_AGENT',
586
+ 'HTTP_X_ORIGINAL_USER_AGENT',
587
+ 'HTTP_X_SKYFIRE_PHONE',
588
+ 'HTTP_X_BOLT_PHONE_UA',
589
+ 'HTTP_DEVICE_STOCK_UA',
590
+ 'HTTP_X_UCBROWSER_DEVICE_UA'
591
+ );
592
+
593
+ /**
594
+ * The individual segments that could exist in a User-Agent string. VER refers to the regular
595
+ * expression defined in the constant self::VER.
596
+ *
597
+ * @var array
598
+ */
599
+ protected static $properties = array(
600
+
601
+ // Build
602
+ 'Mobile' => 'Mobile/[VER]',
603
+ 'Build' => 'Build/[VER]',
604
+ 'Version' => 'Version/[VER]',
605
+ 'VendorID' => 'VendorID/[VER]',
606
+
607
+ // Devices
608
+ 'iPad' => 'iPad.*CPU[a-z ]+[VER]',
609
+ 'iPhone' => 'iPhone.*CPU[a-z ]+[VER]',
610
+ 'iPod' => 'iPod.*CPU[a-z ]+[VER]',
611
+ //'BlackBerry' => array('BlackBerry[VER]', 'BlackBerry [VER];'),
612
+ 'Kindle' => 'Kindle/[VER]',
613
+
614
+ // Browser
615
+ 'Chrome' => array('Chrome/[VER]', 'CriOS/[VER]', 'CrMo/[VER]'),
616
+ 'Coast' => array('Coast/[VER]'),
617
+ 'Dolfin' => 'Dolfin/[VER]',
618
+ // @reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
619
+ 'Firefox' => array('Firefox/[VER]', 'FxiOS/[VER]'),
620
+ 'Fennec' => 'Fennec/[VER]',
621
+ // http://msdn.microsoft.com/en-us/library/ms537503(v=vs.85).aspx
622
+ // https://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx
623
+ 'Edge' => 'Edge/[VER]',
624
+ 'IE' => array('IEMobile/[VER];', 'IEMobile [VER]', 'MSIE [VER];', 'Trident/[0-9.]+;.*rv:[VER]'),
625
+ // http://en.wikipedia.org/wiki/NetFront
626
+ 'NetFront' => 'NetFront/[VER]',
627
+ 'NokiaBrowser' => 'NokiaBrowser/[VER]',
628
+ 'Opera' => array( ' OPR/[VER]', 'Opera Mini/[VER]', 'Version/[VER]' ),
629
+ 'Opera Mini' => 'Opera Mini/[VER]',
630
+ 'Opera Mobi' => 'Version/[VER]',
631
+ 'UC Browser' => 'UC Browser[VER]',
632
+ 'MQQBrowser' => 'MQQBrowser/[VER]',
633
+ 'MicroMessenger' => 'MicroMessenger/[VER]',
634
+ 'baiduboxapp' => 'baiduboxapp/[VER]',
635
+ 'baidubrowser' => 'baidubrowser/[VER]',
636
+ 'SamsungBrowser' => 'SamsungBrowser/[VER]',
637
+ 'Iron' => 'Iron/[VER]',
638
+ // @note: Safari 7534.48.3 is actually Version 5.1.
639
+ // @note: On BlackBerry the Version is overwriten by the OS.
640
+ 'Safari' => array( 'Version/[VER]', 'Safari/[VER]' ),
641
+ 'Skyfire' => 'Skyfire/[VER]',
642
+ 'Tizen' => 'Tizen/[VER]',
643
+ 'Webkit' => 'webkit[ /][VER]',
644
+ 'PaleMoon' => 'PaleMoon/[VER]',
645
+
646
+ // Engine
647
+ 'Gecko' => 'Gecko/[VER]',
648
+ 'Trident' => 'Trident/[VER]',
649
+ 'Presto' => 'Presto/[VER]',
650
+ 'Goanna' => 'Goanna/[VER]',
651
+
652
+ // OS
653
+ 'iOS' => ' \bi?OS\b [VER][ ;]{1}',
654
+ 'Android' => 'Android [VER]',
655
+ 'BlackBerry' => array('BlackBerry[\w]+/[VER]', 'BlackBerry.*Version/[VER]', 'Version/[VER]'),
656
+ 'BREW' => 'BREW [VER]',
657
+ 'Java' => 'Java/[VER]',
658
+ // @reference: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/08/29/introducing-the-ie9-on-windows-phone-mango-user-agent-string.aspx
659
+ // @reference: http://en.wikipedia.org/wiki/Windows_NT#Releases
660
+ 'Windows Phone OS' => array( 'Windows Phone OS [VER]', 'Windows Phone [VER]'),
661
+ 'Windows Phone' => 'Windows Phone [VER]',
662
+ 'Windows CE' => 'Windows CE/[VER]',
663
+ // http://social.msdn.microsoft.com/Forums/en-US/windowsdeveloperpreviewgeneral/thread/6be392da-4d2f-41b4-8354-8dcee20c85cd
664
+ 'Windows NT' => 'Windows NT [VER]',
665
+ 'Symbian' => array('SymbianOS/[VER]', 'Symbian/[VER]'),
666
+ 'webOS' => array('webOS/[VER]', 'hpwOS/[VER];'),
667
+ );
668
+
669
+ /**
670
+ * Construct an instance of this class.
671
+ *
672
+ * @param array $headers Specify the headers as injection. Should be PHP _SERVER flavored.
673
+ * If left empty, will use the global _SERVER['HTTP_*'] vars instead.
674
+ * @param string $userAgent Inject the User-Agent header. If null, will use HTTP_USER_AGENT
675
+ * from the $headers array instead.
676
+ */
677
+ public function __construct(
678
+ array $headers = null,
679
+ $userAgent = null
680
+ ) {
681
+ $this->setHttpHeaders($headers);
682
+ $this->setUserAgent($userAgent);
683
+ }
684
+
685
+ /**
686
+ * Get the current script version.
687
+ * This is useful for the demo.php file,
688
+ * so people can check on what version they are testing
689
+ * for mobile devices.
690
+ *
691
+ * @return string The version number in semantic version format.
692
+ */
693
+ public static function getScriptVersion()
694
+ {
695
+ return self::VERSION;
696
+ }
697
+
698
+ /**
699
+ * Set the HTTP Headers. Must be PHP-flavored. This method will reset existing headers.
700
+ *
701
+ * @param array $httpHeaders The headers to set. If null, then using PHP's _SERVER to extract
702
+ * the headers. The default null is left for backwards compatibility.
703
+ */
704
+ public function setHttpHeaders($httpHeaders = null)
705
+ {
706
+ // use global _SERVER if $httpHeaders aren't defined
707
+ if (!is_array($httpHeaders) || !count($httpHeaders)) {
708
+ $httpHeaders = $_SERVER;
709
+ }
710
+
711
+ // clear existing headers
712
+ $this->httpHeaders = array();
713
+
714
+ // Only save HTTP headers. In PHP land, that means only _SERVER vars that
715
+ // start with HTTP_.
716
+ foreach ($httpHeaders as $key => $value) {
717
+ if (substr($key, 0, 5) === 'HTTP_') {
718
+ $this->httpHeaders[$key] = $value;
719
+ }
720
+ }
721
+
722
+ // In case we're dealing with CloudFront, we need to know.
723
+ $this->setCfHeaders($httpHeaders);
724
+ }
725
+
726
+ /**
727
+ * Retrieves the HTTP headers.
728
+ *
729
+ * @return array
730
+ */
731
+ public function getHttpHeaders()
732
+ {
733
+ return $this->httpHeaders;
734
+ }
735
+
736
+ /**
737
+ * Retrieves a particular header. If it doesn't exist, no exception/error is caused.
738
+ * Simply null is returned.
739
+ *
740
+ * @param string $header The name of the header to retrieve. Can be HTTP compliant such as
741
+ * "User-Agent" or "X-Device-User-Agent" or can be php-esque with the
742
+ * all-caps, HTTP_ prefixed, underscore seperated awesomeness.
743
+ *
744
+ * @return string|null The value of the header.
745
+ */
746
+ public function getHttpHeader($header)
747
+ {
748
+ // are we using PHP-flavored headers?
749
+ if (strpos($header, '_') === false) {
750
+ $header = str_replace('-', '_', $header);
751
+ $header = strtoupper($header);
752
+ }
753
+
754
+ // test the alternate, too
755
+ $altHeader = 'HTTP_' . $header;
756
+
757
+ //Test both the regular and the HTTP_ prefix
758
+ if (isset($this->httpHeaders[$header])) {
759
+ return $this->httpHeaders[$header];
760
+ } elseif (isset($this->httpHeaders[$altHeader])) {
761
+ return $this->httpHeaders[$altHeader];
762
+ }
763
+
764
+ return null;
765
+ }
766
+
767
+ public function getMobileHeaders()
768
+ {
769
+ return self::$mobileHeaders;
770
+ }
771
+
772
+ /**
773
+ * Get all possible HTTP headers that
774
+ * can contain the User-Agent string.
775
+ *
776
+ * @return array List of HTTP headers.
777
+ */
778
+ public function getUaHttpHeaders()
779
+ {
780
+ return self::$uaHttpHeaders;
781
+ }
782
+
783
+
784
+ /**
785
+ * Set CloudFront headers
786
+ * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device
787
+ *
788
+ * @param array $cfHeaders List of HTTP headers
789
+ *
790
+ * @return boolean If there were CloudFront headers to be set
791
+ */
792
+ public function setCfHeaders($cfHeaders = null) {
793
+ // use global _SERVER if $cfHeaders aren't defined
794
+ if (!is_array($cfHeaders) || !count($cfHeaders)) {
795
+ $cfHeaders = $_SERVER;
796
+ }
797
+
798
+ // clear existing headers
799
+ $this->cloudfrontHeaders = array();
800
+
801
+ // Only save CLOUDFRONT headers. In PHP land, that means only _SERVER vars that
802
+ // start with cloudfront-.
803
+ $response = false;
804
+ foreach ($cfHeaders as $key => $value) {
805
+ if (substr(strtolower($key), 0, 16) === 'http_cloudfront_') {
806
+ $this->cloudfrontHeaders[strtoupper($key)] = $value;
807
+ $response = true;
808
+ }
809
+ }
810
+
811
+ return $response;
812
+ }
813
+
814
+ /**
815
+ * Retrieves the cloudfront headers.
816
+ *
817
+ * @return array
818
+ */
819
+ public function getCfHeaders()
820
+ {
821
+ return $this->cloudfrontHeaders;
822
+ }
823
+
824
+ /**
825
+ * Set the User-Agent to be used.
826
+ *
827
+ * @param string $userAgent The user agent string to set.
828
+ *
829
+ * @return string|null
830
+ */
831
+ public function setUserAgent($userAgent = null)
832
+ {
833
+ // Invalidate cache due to #375
834
+ $this->cache = array();
835
+
836
+ if (false === empty($userAgent)) {
837
+ return $this->userAgent = $userAgent;
838
+ } else {
839
+ $this->userAgent = null;
840
+ foreach ($this->getUaHttpHeaders() as $altHeader) {
841
+ if (false === empty($this->httpHeaders[$altHeader])) { // @todo: should use getHttpHeader(), but it would be slow. (Serban)
842
+ $this->userAgent .= $this->httpHeaders[$altHeader] . " ";
843
+ }
844
+ }
845
+
846
+ if (!empty($this->userAgent)) {
847
+ return $this->userAgent = trim($this->userAgent);
848
+ }
849
+ }
850
+
851
+ if (count($this->getCfHeaders()) > 0) {
852
+ return $this->userAgent = 'Amazon CloudFront';
853
+ }
854
+ return $this->userAgent = null;
855
+ }
856
+
857
+ /**
858
+ * Retrieve the User-Agent.
859
+ *
860
+ * @return string|null The user agent if it's set.
861
+ */
862
+ public function getUserAgent()
863
+ {
864
+ return $this->userAgent;
865
+ }
866
+
867
+ /**
868
+ * Set the detection type. Must be one of self::DETECTION_TYPE_MOBILE or
869
+ * self::DETECTION_TYPE_EXTENDED. Otherwise, nothing is set.
870
+ *
871
+ * @deprecated since version 2.6.9
872
+ *
873
+ * @param string $type The type. Must be a self::DETECTION_TYPE_* constant. The default
874
+ * parameter is null which will default to self::DETECTION_TYPE_MOBILE.
875
+ */
876
+ public function setDetectionType($type = null)
877
+ {
878
+ if ($type === null) {
879
+ $type = self::DETECTION_TYPE_MOBILE;
880
+ }
881
+
882
+ if ($type !== self::DETECTION_TYPE_MOBILE && $type !== self::DETECTION_TYPE_EXTENDED) {
883
+ return;
884
+ }
885
+
886
+ $this->detectionType = $type;
887
+ }
888
+
889
+ public function getMatchingRegex()
890
+ {
891
+ return $this->matchingRegex;
892
+ }
893
+
894
+ public function getMatchesArray()
895
+ {
896
+ return $this->matchesArray;
897
+ }
898
+
899
+ /**
900
+ * Retrieve the list of known phone devices.
901
+ *
902
+ * @return array List of phone devices.
903
+ */
904
+ public static function getPhoneDevices()
905
+ {
906
+ return self::$phoneDevices;
907
+ }
908
+
909
+ /**
910
+ * Retrieve the list of known tablet devices.
911
+ *
912
+ * @return array List of tablet devices.
913
+ */
914
+ public static function getTabletDevices()
915
+ {
916
+ return self::$tabletDevices;
917
+ }
918
+
919
+ /**
920
+ * Alias for getBrowsers() method.
921
+ *
922
+ * @return array List of user agents.
923
+ */
924
+ public static function getUserAgents()
925
+ {
926
+ return self::getBrowsers();
927
+ }
928
+
929
+ /**
930
+ * Retrieve the list of known browsers. Specifically, the user agents.
931
+ *
932
+ * @return array List of browsers / user agents.
933
+ */
934
+ public static function getBrowsers()
935
+ {
936
+ return self::$browsers;
937
+ }
938
+
939
+ /**
940
+ * Retrieve the list of known utilities.
941
+ *
942
+ * @return array List of utilities.
943
+ */
944
+ public static function getUtilities()
945
+ {
946
+ return self::$utilities;
947
+ }
948
+
949
+ /**
950
+ * Method gets the mobile detection rules. This method is used for the magic methods $detect->is*().
951
+ *
952
+ * @deprecated since version 2.6.9
953
+ *
954
+ * @return array All the rules (but not extended).
955
+ */
956
+ public static function getMobileDetectionRules()
957
+ {
958
+ static $rules;
959
+
960
+ if (!$rules) {
961
+ $rules = array_merge(
962
+ self::$phoneDevices,
963
+ self::$tabletDevices,
964
+ self::$operatingSystems,
965
+ self::$browsers
966
+ );
967
+ }
968
+
969
+ return $rules;
970
+
971
+ }
972
+
973
+ /**
974
+ * Method gets the mobile detection rules + utilities.
975
+ * The reason this is separate is because utilities rules
976
+ * don't necessary imply mobile. This method is used inside
977
+ * the new $detect->is('stuff') method.
978
+ *
979
+ * @deprecated since version 2.6.9
980
+ *
981
+ * @return array All the rules + extended.
982
+ */
983
+ public function getMobileDetectionRulesExtended()
984
+ {
985
+ static $rules;
986
+
987
+ if (!$rules) {
988
+ // Merge all rules together.
989
+ $rules = array_merge(
990
+ self::$phoneDevices,
991
+ self::$tabletDevices,
992
+ self::$operatingSystems,
993
+ self::$browsers,
994
+ self::$utilities
995
+ );
996
+ }
997
+
998
+ return $rules;
999
+ }
1000
+
1001
+ /**
1002
+ * Retrieve the current set of rules.
1003
+ *
1004
+ * @deprecated since version 2.6.9
1005
+ *
1006
+ * @return array
1007
+ */
1008
+ public function getRules()
1009
+ {
1010
+ if ($this->detectionType == self::DETECTION_TYPE_EXTENDED) {
1011
+ return self::getMobileDetectionRulesExtended();
1012
+ } else {
1013
+ return self::getMobileDetectionRules();
1014
+ }
1015
+ }
1016
+
1017
+ /**
1018
+ * Retrieve the list of mobile operating systems.
1019
+ *
1020
+ * @return array The list of mobile operating systems.
1021
+ */
1022
+ public static function getOperatingSystems()
1023
+ {
1024
+ return self::$operatingSystems;
1025
+ }
1026
+
1027
+ /**
1028
+ * Check the HTTP headers for signs of mobile.
1029
+ * This is the fastest mobile check possible; it's used
1030
+ * inside isMobile() method.
1031
+ *
1032
+ * @return bool
1033
+ */
1034
+ public function checkHttpHeadersForMobile()
1035
+ {
1036
+
1037
+ foreach ($this->getMobileHeaders() as $mobileHeader => $matchType) {
1038
+ if (isset($this->httpHeaders[$mobileHeader])) {
1039
+ if (is_array($matchType['matches'])) {
1040
+ foreach ($matchType['matches'] as $_match) {
1041
+ if (strpos($this->httpHeaders[$mobileHeader], $_match) !== false) {
1042
+ return true;
1043
+ }
1044
+ }
1045
+
1046
+ return false;
1047
+ } else {
1048
+ return true;
1049
+ }
1050
+ }
1051
+ }
1052
+
1053
+ return false;
1054
+
1055
+ }
1056
+
1057
+ /**
1058
+ * Magic overloading method.
1059
+ *
1060
+ * @method boolean is[...]()
1061
+ * @param string $name
1062
+ * @param array $arguments
1063
+ * @return mixed
1064
+ * @throws BadMethodCallException when the method doesn't exist and doesn't start with 'is'
1065
+ */
1066
+ public function __call($name, $arguments)
1067
+ {
1068
+ // make sure the name starts with 'is', otherwise
1069
+ if (substr($name, 0, 2) !== 'is') {
1070
+ throw new BadMethodCallException("No such method exists: $name");
1071
+ }
1072
+
1073
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
1074
+
1075
+ $key = substr($name, 2);
1076
+
1077
+ return $this->matchUAAgainstKey($key);
1078
+ }
1079
+
1080
+ /**
1081
+ * Find a detection rule that matches the current User-agent.
1082
+ *
1083
+ * @param null $userAgent deprecated
1084
+ * @return boolean
1085
+ */
1086
+ protected function matchDetectionRulesAgainstUA($userAgent = null)
1087
+ {
1088
+ // Begin general search.
1089
+ foreach ($this->getRules() as $_regex) {
1090
+ if (empty($_regex)) {
1091
+ continue;
1092
+ }
1093
+
1094
+ if ($this->match($_regex, $userAgent)) {
1095
+ return true;
1096
+ }
1097
+ }
1098
+
1099
+ return false;
1100
+ }
1101
+
1102
+ /**
1103
+ * Search for a certain key in the rules array.
1104
+ * If the key is found then try to match the corresponding
1105
+ * regex against the User-Agent.
1106
+ *
1107
+ * @param string $key
1108
+ *
1109
+ * @return boolean
1110
+ */
1111
+ protected function matchUAAgainstKey($key)
1112
+ {
1113
+ // Make the keys lowercase so we can match: isIphone(), isiPhone(), isiphone(), etc.
1114
+ $key = strtolower($key);
1115
+ if (false === isset($this->cache[$key])) {
1116
+
1117
+ // change the keys to lower case
1118
+ $_rules = array_change_key_case($this->getRules());
1119
+
1120
+ if (false === empty($_rules[$key])) {
1121
+ $this->cache[$key] = $this->match($_rules[$key]);
1122
+ }
1123
+
1124
+ if (false === isset($this->cache[$key])) {
1125
+ $this->cache[$key] = false;
1126
+ }
1127
+ }
1128
+
1129
+ return $this->cache[$key];
1130
+ }
1131
+
1132
+ /**
1133
+ * Check if the device is mobile.
1134
+ * Returns true if any type of mobile device detected, including special ones
1135
+ * @param null $userAgent deprecated
1136
+ * @param null $httpHeaders deprecated
1137
+ * @return bool
1138
+ */
1139
+ public function isMobile($userAgent = null, $httpHeaders = null)
1140
+ {
1141
+
1142
+ if ($httpHeaders) {
1143
+ $this->setHttpHeaders($httpHeaders);
1144
+ }
1145
+
1146
+ if ($userAgent) {
1147
+ $this->setUserAgent($userAgent);
1148
+ }
1149
+
1150
+ // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
1151
+ if ($this->getUserAgent() === 'Amazon CloudFront') {
1152
+ $cfHeaders = $this->getCfHeaders();
1153
+ if(array_key_exists('HTTP_CLOUDFRONT_IS_MOBILE_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_MOBILE_VIEWER'] === 'true') {
1154
+ return true;
1155
+ }
1156
+ }
1157
+
1158
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
1159
+
1160
+ if ($this->checkHttpHeadersForMobile()) {
1161
+ return true;
1162
+ } else {
1163
+ return $this->matchDetectionRulesAgainstUA();
1164
+ }
1165
+
1166
+ }
1167
+
1168
+ /**
1169
+ * Check if the device is a tablet.
1170
+ * Return true if any type of tablet device is detected.
1171
+ *
1172
+ * @param string $userAgent deprecated
1173
+ * @param array $httpHeaders deprecated
1174
+ * @return bool
1175
+ */
1176
+ public function isTablet($userAgent = null, $httpHeaders = null)
1177
+ {
1178
+ // Check specifically for cloudfront headers if the useragent === 'Amazon CloudFront'
1179
+ if ($this->getUserAgent() === 'Amazon CloudFront') {
1180
+ $cfHeaders = $this->getCfHeaders();
1181
+ if(array_key_exists('HTTP_CLOUDFRONT_IS_TABLET_VIEWER', $cfHeaders) && $cfHeaders['HTTP_CLOUDFRONT_IS_TABLET_VIEWER'] === 'true') {
1182
+ return true;
1183
+ }
1184
+ }
1185
+
1186
+ $this->setDetectionType(self::DETECTION_TYPE_MOBILE);
1187
+
1188
+ foreach (self::$tabletDevices as $_regex) {
1189
+ if ($this->match($_regex, $userAgent)) {
1190
+ return true;
1191
+ }
1192
+ }
1193
+
1194
+ return false;
1195
+ }
1196
+
1197
+ /**
1198
+ * This method checks for a certain property in the
1199
+ * userAgent.
1200
+ * @todo: The httpHeaders part is not yet used.
1201
+ *
1202
+ * @param string $key
1203
+ * @param string $userAgent deprecated
1204
+ * @param string $httpHeaders deprecated
1205
+ * @return bool|int|null
1206
+ */
1207
+ public function is($key, $userAgent = null, $httpHeaders = null)
1208
+ {
1209
+ // Set the UA and HTTP headers only if needed (eg. batch mode).
1210
+ if ($httpHeaders) {
1211
+ $this->setHttpHeaders($httpHeaders);
1212
+ }
1213
+
1214
+ if ($userAgent) {
1215
+ $this->setUserAgent($userAgent);
1216
+ }
1217
+
1218
+ $this->setDetectionType(self::DETECTION_TYPE_EXTENDED);
1219
+
1220
+ return $this->matchUAAgainstKey($key);
1221
+ }
1222
+
1223
+ /**
1224
+ * Some detection rules are relative (not standard),
1225
+ * because of the diversity of devices, vendors and
1226
+ * their conventions in representing the User-Agent or
1227
+ * the HTTP headers.
1228
+ *
1229
+ * This method will be used to check custom regexes against
1230
+ * the User-Agent string.
1231
+ *
1232
+ * @param $regex
1233
+ * @param string $userAgent
1234
+ * @return bool
1235
+ *
1236
+ * @todo: search in the HTTP headers too.
1237
+ */
1238
+ public function match($regex, $userAgent = null)
1239
+ {
1240
+ $match = (bool) preg_match(sprintf('#%s#is', $regex), (false === empty($userAgent) ? $userAgent : $this->userAgent), $matches);
1241
+ // If positive match is found, store the results for debug.
1242
+ if ($match) {
1243
+ $this->matchingRegex = $regex;
1244
+ $this->matchesArray = $matches;
1245
+ }
1246
+
1247
+ return $match;
1248
+ }
1249
+
1250
+ /**
1251
+ * Get the properties array.
1252
+ *
1253
+ * @return array
1254
+ */
1255
+ public static function getProperties()
1256
+ {
1257
+ return self::$properties;
1258
+ }
1259
+
1260
+ /**
1261
+ * Prepare the version number.
1262
+ *
1263
+ * @todo Remove the error supression from str_replace() call.
1264
+ *
1265
+ * @param string $ver The string version, like "2.6.21.2152";
1266
+ *
1267
+ * @return float
1268
+ */
1269
+ public function prepareVersionNo($ver)
1270
+ {
1271
+ $ver = str_replace(array('_', ' ', '/'), '.', $ver);
1272
+ $arrVer = explode('.', $ver, 2);
1273
+
1274
+ if (isset($arrVer[1])) {
1275
+ $arrVer[1] = @str_replace('.', '', $arrVer[1]); // @todo: treat strings versions.
1276
+ }
1277
+
1278
+ return (float) implode('.', $arrVer);
1279
+ }
1280
+
1281
+ /**
1282
+ * Check the version of the given property in the User-Agent.
1283
+ * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
1284
+ *
1285
+ * @param string $propertyName The name of the property. See self::getProperties() array
1286
+ * keys for all possible properties.
1287
+ * @param string $type Either self::VERSION_TYPE_STRING to get a string value or
1288
+ * self::VERSION_TYPE_FLOAT indicating a float value. This parameter
1289
+ * is optional and defaults to self::VERSION_TYPE_STRING. Passing an
1290
+ * invalid parameter will default to the this type as well.
1291
+ *
1292
+ * @return string|float The version of the property we are trying to extract.
1293
+ */
1294
+ public function version($propertyName, $type = self::VERSION_TYPE_STRING)
1295
+ {
1296
+ if (empty($propertyName)) {
1297
+ return false;
1298
+ }
1299
+
1300
+ // set the $type to the default if we don't recognize the type
1301
+ if ($type !== self::VERSION_TYPE_STRING && $type !== self::VERSION_TYPE_FLOAT) {
1302
+ $type = self::VERSION_TYPE_STRING;
1303
+ }
1304
+
1305
+ $properties = self::getProperties();
1306
+
1307
+ // Check if the property exists in the properties array.
1308
+ if (true === isset($properties[$propertyName])) {
1309
+
1310
+ // Prepare the pattern to be matched.
1311
+ // Make sure we always deal with an array (string is converted).
1312
+ $properties[$propertyName] = (array) $properties[$propertyName];
1313
+
1314
+ foreach ($properties[$propertyName] as $propertyMatchString) {
1315
+
1316
+ $propertyPattern = str_replace('[VER]', self::VER, $propertyMatchString);
1317
+
1318
+ // Identify and extract the version.
1319
+ preg_match(sprintf('#%s#is', $propertyPattern), $this->userAgent, $match);
1320
+
1321
+ if (false === empty($match[1])) {
1322
+ $version = ($type == self::VERSION_TYPE_FLOAT ? $this->prepareVersionNo($match[1]) : $match[1]);
1323
+
1324
+ return $version;
1325
+ }
1326
+
1327
+ }
1328
+
1329
+ }
1330
+
1331
+ return false;
1332
+ }
1333
+
1334
+ /**
1335
+ * Retrieve the mobile grading, using self::MOBILE_GRADE_* constants.
1336
+ *
1337
+ * @return string One of the self::MOBILE_GRADE_* constants.
1338
+ */
1339
+ public function mobileGrade()
1340
+ {
1341
+ $isMobile = $this->isMobile();
1342
+
1343
+ if (
1344
+ // Apple iOS 4-7.0 – Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), iPad Retina (7.0), iPhone 3GS (4.3), iPhone 4 (4.3 / 5.1), iPhone 4S (5.1 / 6.0), iPhone 5 (6.0), and iPhone 5S (7.0)
1345
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) >= 4.3 ||
1346
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) >= 4.3 ||
1347
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) >= 4.3 ||
1348
+
1349
+ // 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)
1350
+ // Android 3.1 (Honeycomb) - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
1351
+ // Android 4.0 (ICS) - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
1352
+ // Android 4.1 (Jelly Bean) - Tested on a Galaxy Nexus and Galaxy 7
1353
+ ( $this->version('Android', self::VERSION_TYPE_FLOAT)>2.1 && $this->is('Webkit') ) ||
1354
+
1355
+ // Windows Phone 7.5-8 - Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia 800 (7.8), HTC Mazaa (7.8), Nokia Lumia 520 (8), Nokia Lumia 920 (8), HTC 8x (8)
1356
+ $this->version('Windows Phone OS', self::VERSION_TYPE_FLOAT) >= 7.5 ||
1357
+
1358
+ // Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10)
1359
+ $this->is('BlackBerry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 6.0 ||
1360
+ // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
1361
+ $this->match('Playbook.*Tablet') ||
1362
+
1363
+ // Palm WebOS (1.4-3.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad (3.0)
1364
+ ( $this->version('webOS', self::VERSION_TYPE_FLOAT) >= 1.4 && $this->match('Palm|Pre|Pixi') ) ||
1365
+ // Palm WebOS 3.0 - Tested on HP TouchPad
1366
+ $this->match('hp.*TouchPad') ||
1367
+
1368
+ // Firefox Mobile 18 - Tested on Android 2.3 and 4.1 devices
1369
+ ( $this->is('Firefox') && $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 18 ) ||
1370
+
1371
+ // Chrome for Android - Tested on Android 4.0, 4.1 device
1372
+ ( $this->is('Chrome') && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 4.0 ) ||
1373
+
1374
+ // Skyfire 4.1 - Tested on Android 2.3 device
1375
+ ( $this->is('Skyfire') && $this->version('Skyfire', self::VERSION_TYPE_FLOAT) >= 4.1 && $this->is('AndroidOS') && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
1376
+
1377
+ // Opera Mobile 11.5-12: Tested on Android 2.3
1378
+ ( $this->is('Opera') && $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11.5 && $this->is('AndroidOS') ) ||
1379
+
1380
+ // Meego 1.2 - Tested on Nokia 950 and N9
1381
+ $this->is('MeeGoOS') ||
1382
+
1383
+ // Tizen (pre-release) - Tested on early hardware
1384
+ $this->is('Tizen') ||
1385
+
1386
+ // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
1387
+ // @todo: more tests here!
1388
+ $this->is('Dolfin') && $this->version('Bada', self::VERSION_TYPE_FLOAT) >= 2.0 ||
1389
+
1390
+ // UC Browser - Tested on Android 2.3 device
1391
+ ( ($this->is('UC Browser') || $this->is('Dolfin')) && $this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 ) ||
1392
+
1393
+ // Kindle 3 and Fire - Tested on the built-in WebKit browser for each
1394
+ ( $this->match('Kindle Fire') ||
1395
+ $this->is('Kindle') && $this->version('Kindle', self::VERSION_TYPE_FLOAT) >= 3.0 ) ||
1396
+
1397
+ // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
1398
+ $this->is('AndroidOS') && $this->is('NookTablet') ||
1399
+
1400
+ // Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7
1401
+ $this->version('Chrome', self::VERSION_TYPE_FLOAT) >= 16 && !$isMobile ||
1402
+
1403
+ // Safari Desktop 5-6 - Tested on OS X 10.7 and Windows 7
1404
+ $this->version('Safari', self::VERSION_TYPE_FLOAT) >= 5.0 && !$isMobile ||
1405
+
1406
+ // Firefox Desktop 10-18 - Tested on OS X 10.7 and Windows 7
1407
+ $this->version('Firefox', self::VERSION_TYPE_FLOAT) >= 10.0 && !$isMobile ||
1408
+
1409
+ // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
1410
+ $this->version('IE', self::VERSION_TYPE_FLOAT) >= 7.0 && !$isMobile ||
1411
+
1412
+ // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
1413
+ $this->version('Opera', self::VERSION_TYPE_FLOAT) >= 10 && !$isMobile
1414
+ ){
1415
+ return self::MOBILE_GRADE_A;
1416
+ }
1417
+
1418
+ if (
1419
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT)<4.3 ||
1420
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT)<4.3 ||
1421
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT)<4.3 ||
1422
+
1423
+ // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
1424
+ $this->is('Blackberry') && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) >= 5 && $this->version('BlackBerry', self::VERSION_TYPE_FLOAT)<6 ||
1425
+
1426
+ //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
1427
+ ($this->version('Opera Mini', self::VERSION_TYPE_FLOAT) >= 5.0 && $this->version('Opera Mini', self::VERSION_TYPE_FLOAT) <= 7.0 &&
1428
+ ($this->version('Android', self::VERSION_TYPE_FLOAT) >= 2.3 || $this->is('iOS')) ) ||
1429
+
1430
+ // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
1431
+ $this->match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
1432
+
1433
+ // @todo: report this (tested on Nokia N71)
1434
+ $this->version('Opera Mobi', self::VERSION_TYPE_FLOAT) >= 11 && $this->is('SymbianOS')
1435
+ ){
1436
+ return self::MOBILE_GRADE_B;
1437
+ }
1438
+
1439
+ if (
1440
+ // Blackberry 4.x - Tested on the Curve 8330
1441
+ $this->version('BlackBerry', self::VERSION_TYPE_FLOAT) <= 5.0 ||
1442
+ // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
1443
+ $this->match('MSIEMobile|Windows CE.*Mobile') || $this->version('Windows Mobile', self::VERSION_TYPE_FLOAT) <= 5.2 ||
1444
+
1445
+ // Tested on original iPhone (3.1), iPhone 3 (3.2)
1446
+ $this->is('iOS') && $this->version('iPad', self::VERSION_TYPE_FLOAT) <= 3.2 ||
1447
+ $this->is('iOS') && $this->version('iPhone', self::VERSION_TYPE_FLOAT) <= 3.2 ||
1448
+ $this->is('iOS') && $this->version('iPod', self::VERSION_TYPE_FLOAT) <= 3.2 ||
1449
+
1450
+ // Internet Explorer 7 and older - Tested on Windows XP
1451
+ $this->version('IE', self::VERSION_TYPE_FLOAT) <= 7.0 && !$isMobile
1452
+ ){
1453
+ return self::MOBILE_GRADE_C;
1454
+ }
1455
+
1456
+ // All older smartphone platforms and featurephones - Any device that doesn't support media queries
1457
+ // will receive the basic, C grade experience.
1458
+ return self::MOBILE_GRADE_C;
1459
+ }
1460
+ }
inc/cache/Mobile-Detect-2.8.25/README.md ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ![Mobile Detect](http://demo.mobiledetect.net/logo-github.png)
2
+
3
+ > Motto: "Every business should have a mobile detection script to detect mobile readers."
4
+
5
+ [![Build Status](https://travis-ci.org/serbanghita/Mobile-Detect.svg?branch=devel)](https://travis-ci.org/serbanghita/Mobile-Detect)
6
+ [![Latest Stable Version](https://poser.pugx.org/mobiledetect/mobiledetectlib/v/stable.svg)](https://packagist.org/packages/mobiledetect/mobiledetectlib)
7
+ [![Total Downloads](https://poser.pugx.org/mobiledetect/mobiledetectlib/downloads.svg)](https://packagist.org/packages/mobiledetect/mobiledetectlib)
8
+ [![Daily Downloads](https://poser.pugx.org/mobiledetect/mobiledetectlib/d/daily.png)](https://packagist.org/packages/mobiledetect/mobiledetectlib)
9
+ [![License](https://poser.pugx.org/mobiledetect/mobiledetectlib/license.svg)](https://packagist.org/packages/mobiledetect/mobiledetectlib)
10
+
11
+ *Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
12
+ It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.*
13
+
14
+ We're committed to make Mobile_Detect the best open-source mobile detection resource and this is why before
15
+ each release we're running [unit tests](./tests), we also research and update the detection rules on **daily**
16
+ and **weekly** basis.
17
+
18
+ Your website's _content strategy_ is important! You need a complete toolkit to deliver an experience that is _optimized_, _fast_ and _relevant_ to your users. Mobile_Detect class is a [server-side detection](http://www.w3.org/TR/mwabp/#bp-devcap-detection) tool that can help you with your RWD strategy, it is not a replacement for CSS3 media queries or other forms of client-side feature detection.
19
+
20
+ ##### Announcements
21
+
22
+ For `2.x` branch we are no longer taking optimizations pull requests, but only new regexes and User-Agents for our tests.
23
+ On `2.x` releases we are focusing on **new tablets only**. All the pull requests about TVs, bots or optimizations will be closed and analyzed after `3.0.0-beta` is released.
24
+
25
+ Still working on `3.0.0` branch to provide you with device detection!
26
+ We're really excited on this one!
27
+ We would like to speed this up, but life and family gets in the way ;)
28
+
29
+ Special thanks to **JetBrains** for providing licenses for **PHPStorm**. In case you never heard or tried PHPStorm, you're
30
+ clearly missing out! [Check PHPStorm](https://www.jetbrains.com/phpstorm/) out!
31
+
32
+ ##### Download and demo
33
+
34
+ |Download|Docs|Examples|
35
+ |-------------|-------------|-------------|
36
+ |[Go to releases](../../tags)|[Become a contributor](../../wiki/Become-a-contributor)|[Code examples](../../wiki/Code-examples)
37
+ |[Mobile_Detect.php](./Mobile_Detect.php)|[History](../../wiki/History)|[:iphone: Live demo!](http://is.gd/mobiletest)
38
+ |[Composer package](https://packagist.org/packages/mobiledetect/mobiledetectlib)|
39
+
40
+ #### Continuous updates
41
+
42
+ You can use [composer](https://getcomposer.org/doc/00-intro.md) in your release and update process to make sure you have the latest Mobile_Detect version.
43
+
44
+ ```
45
+ composer require mobiledetect/mobiledetectlib
46
+ ```
47
+
48
+ ```json
49
+ {
50
+ "require": {
51
+ "mobiledetect/mobiledetectlib": "^2.8"
52
+ }
53
+ }
54
+ ```
55
+
56
+ ##### Help
57
+
58
+ |Pledgie|Paypal|
59
+ |-------|------|
60
+ |[Donate :+1:](https://pledgie.com/campaigns/21856)|[Donate :beer:](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mobiledetectlib%40gmail%2ecom&lc=US&item_name=Mobile%20Detect&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted)|
61
+
62
+
63
+ I'm currently paying for hosting and spend a lot of my family time to maintain the project and planning the future releases.
64
+ I would highly appreciate any money donations that will keep the research going.
65
+
66
+ Special thanks to the community :+1: for donations, [BrowserStack](https://www.browserstack.com/) - for providing access to their great platform, [Zend](http://www.zend.com/) - for donating licenses, [Dragos Gavrila](https://twitter.com/grafician) who contributed with the logo.
67
+
68
+ ##### 3rd party modules / [Submit new](../../issues/new?title=New%203rd%20party%20module&body=Name, Link and Description of the module.)
69
+
70
+ :point_right: Keep `Mobile_Detect.php` class in a separate `module` and do NOT include it in your script core because of the high frequency of updates.
71
+ :point_right: When including the class into you `web application` or `module` always use `include_once '../path/to/Mobile_Detect.php` to prevent conflicts.
72
+
73
+ **JavaScript**
74
+
75
+ * mobile-detect.js - A [JavaScript port](https://github.com/hgoebl/mobile-detect.js) of Mobile-Detect class. Made by [Heinrich Goebl](https://github.com/hgoebl).
76
+
77
+ **Varnish Cache**
78
+
79
+ * [Varnish Mobile Detect](https://github.com/willemk/varnish-mobiletranslate) - Drop-in varnish solution to mobile user
80
+ detection based on the Mobile-Detect library. Made by [willemk](https://github.com/willemk).
81
+ * [mobiledetect2vcl](https://github.com/carlosabalde/mobiledetect2vcl) - Python script to transform the Mobile
82
+ Detect JSON database into an UA-based mobile detection VCL subroutine easily integrable in any Varnish Cache
83
+ configuration. Made by [Carlos Abalde](https://github.com/carlosabalde).
84
+
85
+ **LUA**
86
+
87
+ * [mobile-detect.lua](https://github.com/yourpalmark/mobile-detect.lua) is a port of Mobile-Detect to Lua for
88
+ NGINX HTTP servers. Follows closely to mobile-detect.js. Supports all methods that server-side
89
+ mobile-detect.js supports. Fully unit-tested and synced with Travis CI (Build Passing badge included).
90
+ Made by [Mark Walters](https://github.com/yourpalmark).
91
+
92
+ **PHP**
93
+
94
+ **WordPress**
95
+
96
+ * [WordPress Mobile Detect](https://wordpress.org/plugins/wp-mobile-detect/) - Gives you the ability to wrap that
97
+ infographic in a `[notdevice][/notdevice]` shortcode so at the server level WordPress will
98
+ decide to show that content only if the user is NOT on a phone or tablet.
99
+ Made by [Jesse Friedman](https://profiles.wordpress.org/professor44/).
100
+
101
+ * [mobble](https://wordpress.org/plugins/mobble/) - provides mobile related conditional functions for your site.
102
+ e.g. `is_iphone()`, `is_mobile()` and `is_tablet()`. Made by Scott Evans.
103
+
104
+ * [WordPress Responsage](https://github.com/iamspacehead/responsage) - A small WordPress theme plugin that allows
105
+ you to make your images responsive. Made by [Adrian Ciaschetti](https://github.com/iamspacehead).
106
+
107
+ * [WP247 Body Classes](https://wordpress.org/plugins/wp247-body-classes/) - Add unique classes to the `body` tag for
108
+ easy styling based on various attributes (archive, user, post, mobile) and various WordPress "is" functions.
109
+ Mobile attributes include type of device, Operating System, Browser, etc. Examples: .is-mobile, .is-not-mobile,
110
+ .is-tablet, .is-ios, .is-not-ios, .is-androidos, .is-chromebrowser.
111
+ Made by [wescleveland56](https://github.com/wescleveland56).
112
+
113
+ **Drupal**
114
+
115
+ * [Drupal Mobile Switch](https://www.drupal.org/project/mobile_switch) - The Mobile Switch Drupal module provides a
116
+ automatic theme switch functionality for mobile devices, detected by Browscap or Mobile Detect.
117
+ Made by [Siegfried Neumann](https://www.drupal.org/user/45267).
118
+
119
+ * [Drupal Context Mobile Detect](https://www.drupal.org/project/context_mobile_detect) - This is a Drupal context module
120
+ which integrates Context and PHP Mobile Detect library.
121
+ Created by [Artem Shymko](https://www.drupal.org/user/432492).
122
+
123
+ * [Drupal Mobile Detect](https://www.drupal.org/project/mobile_detect]) - Lightweight mobile detect module for Drupal
124
+ created by [Matthew Donadio](https://www.drupal.org/user/325244).
125
+
126
+ **Joomla**
127
+
128
+ * [yagendoo Joomla! Mobile Detection Plugin](http://www.yagendoo.com/en/blog/free-mobile-detection-plugin-for-joomla.html) - Lightweight PHP plugin for Joomla!
129
+ that detects a mobile browser using the Mobile Detect class.
130
+ Made by yagendoo media.
131
+
132
+ * [User Agent Detector plugin](https://github.com/renekreijveld/UserAgentDetector) - This system plugin detects the user
133
+ agent of your website visitor and sets a session variable accordingly. Based on the user agent, the plugin detects if the
134
+ site is running on a desktop pc, tablet or smartphone. It can also detect if the visitor is a spider bot (search engine).
135
+ Session variable that is set: `ualayout`. Possible values: desktop, tablet, mobile, bot.
136
+ Made by @ReneKreijveld.
137
+
138
+ **Magento**
139
+
140
+ * [Magento helper](http://www.magentocommerce.com/magento-connect/catalog/product/view/id/16835/) from Optimise Web enables
141
+ the use of all functions provided by Mobile Detect. Made by [Kathir Vel](http://www.kathirvel.com).
142
+
143
+ * [Magento 2 Mobile Detect Theme Change](https://github.com/EaDesgin/magento2-mobiledetect) is an extension for Magento 2
144
+ that will change the theme or redirect to a different URL. Also containing a helper to check for the device type.
145
+
146
+ **PrestaShop**
147
+
148
+ * [PrestaShop](https://www.prestashop.com) is a free, secure and open source shopping cart platform. Mobile_Detect
149
+ is included in the default package since 1.5.x.
150
+
151
+ **Laravel**
152
+
153
+ * [Agent](https://github.com/jenssegers/agent) is a user agent class for Laravel based on Mobile Detect with some
154
+ additional functionality.
155
+ Made by [Jens Segers](https://github.com/jenssegers).
156
+
157
+ * [BrowserDetect](https://github.com/hisorange/browser-detect) is a browser and mobile detection package, collects
158
+ and wrap together the best user-agent identifiers for Laravel.
159
+ Created by [Varga Zsolt](https://github.com/hisorange).
160
+
161
+ **Zend Framework**
162
+
163
+ * [ZF2 Mobile-Detect](https://github.com/neilime/zf2-mobile-detect.git) is a Zend Framework 2 module that provides
164
+ Mobile-Detect features (Mobile_Detect class as a service, helper for views and plugin controllers).
165
+ Made by [neilime](https://github.com/neilime).
166
+
167
+ * [ZF2 MobileDetectModule](https://github.com/nikolaposa/MobileDetectModule) facilitates integration of a PHP MobileDetect
168
+ class with some ZF2-based application. Has similar idea like the existing ZF2 Mobile-Detect module,
169
+ but differs in initialization and provision routine of the actual Mobile_Detect class.
170
+ Appropriate view helper and controller plugin also have different conceptions.
171
+ Made by [Nikola Posa](https://github.com/nikolaposa).
172
+
173
+ **Symfony**
174
+
175
+ * [Symfony2 Mobile Detect Bundle](https://github.com/suncat2000/MobileDetectBundle) is a bundle for detecting mobile devices,
176
+ manage mobile view and redirect to the mobile and tablet version.
177
+ Made by [Nikolay Ivlev](https://github.com/suncat2000).
178
+
179
+ * [Silex Mobile Detect Service Provider](https://github.com/jbinfo/MobileDetectServiceProvider) is a service provider to
180
+ interact with Mobile detect class methods.
181
+ Made by [Lhassan Baazzi](https://github.com/jbinfo).
182
+
183
+ **Slim Framework**
184
+
185
+ * [Slim_Mobile_Detect](https://github.com/zguillez/slim_mobile_detect) implements Mobile_Detect lib for different
186
+ responses write on Slim Framework App.
187
+
188
+ **ExpressionEngine**
189
+
190
+ * [EE2 Detect Mobile](https://github.com/garethtdavies/detect-mobile) is a lightweight PHP plugin for EE2 that detects
191
+ a mobile browser using the Mobile Detect class. Made by [Gareth Davies](https://github.com/garethtdavies).
192
+
193
+ **Yii Framework**
194
+
195
+ * [Yii Extension](https://github.com/iamsalnikov/MobileDetect) - Mobile detect plugin for Yii framework.
196
+ Made by [Alexey Salnikov](https://github.com/iamsalnikov).
197
+
198
+ * [Yii Extension](https://github.com/candasm/yii1-mobile-detect-component) - Mobile detect component for Yii framework
199
+ 1.x version which supports composer package manager. Made by [Candas Minareci](https://github.com/candasm).
200
+
201
+ * [Yii2 Device Detect](https://github.com/alexandernst/yii2-device-detect/) - Yii2 extension for Mobile-Detect library.
202
+ Made by [Alexander Nestorov](https://github.com/alexandernst).
203
+
204
+ **CakePHP**
205
+
206
+ * [CakePHP MobileDetect](https://github.com/chronon/CakePHP-MobileDetectComponent-Plugin) is a plugin component for
207
+ CakePHP 2.x. Made by [Gregory Gaskill](https://github.com/chronon).
208
+
209
+ **FuelPHP**
210
+
211
+ * [Special Agent](https://github.com/rob-bar/special_agent) is a FuelPHP package which uses php-mobile-detect to
212
+ determine whether a device is mobile or not. It overrides the Fuelphp Agent class its methods.
213
+ Made by [Robbie Bardjin](https://github.com/rob-bar).
214
+
215
+
216
+ **TYPO3**
217
+
218
+ * [px_mobiledetect](https://typo3.org/extensions/repository/view/px_mobiledetect) is an extension that helps to detect
219
+ visitor's mobile device class (if that’s tablet or mobile device like smartphone). Made by Alexander Tretyak.
220
+
221
+ **Other**
222
+
223
+ * [PageCache](https://github.com/mmamedov/page-cache) is a lightweight PHP library for full page cache,
224
+ with built-in Mobile-Detect support. Made by [Muhammed Mamedov](https://github.com/mmamedov).
225
+
226
+ * [Statamic CMS Mobile Detect](https://github.com/haikulab/statamic-mobile-detect) is a plugin.
227
+ Made by [Sergei Filippov](https://github.com/haikulab/statamic-mobile-detect) of Haiku Lab.
228
+
229
+ * [Kohana Mobile Detect](https://github.com/madeinnordeste/kohana-mobile-detect) is an example of implementation of
230
+ Mobile_Detect class with Kohana framework.
231
+ Written by [Luiz Alberto S. Ribeiro](https://github.com/madeinnordeste).
232
+
233
+ * [MemHT](https://www.memht.com) is a Free PHP CMS and Blog that permit the creation and the management online
234
+ of websites with few and easy steps. Has the class included in the core.
235
+
236
+ * [concrete5](https://www.concrete5.org) is a CMS that is free and open source. The library is included in the core.
237
+
238
+ * [engine7](https://github.com/QOXCorp/exengine) is PHP Open Source Framework. The Mobile_Detect class is included in
239
+ the engine.
240
+
241
+ * [Zikula](http://zikula.org) is a free and open-source Content Management Framework, which allows you to run
242
+ impressive websites and build powerful online applications. The core uses Mobile-Detect to switch to a special
243
+ Mobile theme, using jQueryMobile.
244
+
245
+ * [UserAgentInfo](https://github.com/quentin389/UserAgentInfo) is a PHP class for parsing user agent strings
246
+ (HTTP_USER_AGENT). Includes mobile checks, bot checks, browser types/versions and more.
247
+ Based on browscap, Mobile_Detect and ua-parser. Created for high traffic websites and fast batch processing.
248
+ Made by [quentin389](https://github.com/quentin389).
249
+
250
+ * [LJ Mobile Detect](https://github.com/lewisjenkins/craft-lj-mobiledetect) is a simple implementation of Mobile Detect
251
+ for Craft CMS. Made by [Lewis Jenkins](https://github.com/lewisjenkins).
252
+
253
+ * [Grav Plugin Mobile Detect](https://github.com/dimitrilongo/grav-plugin-mobile-detect/) is a simple implementation
254
+ of Mobile Detect for Grav CMS. Made by [Dimitri Longo](https://github.com/dimitrilongo).
255
+
256
+
257
+ **Perl**
258
+
259
+ * [MobileDetect.pm](https://www.buzzerstar.com/development/) is a Perl module for Mobile Detect.
260
+ Made by [Sebastian Enger](https://devop.tools/).
261
+
262
+ **Python**
263
+
264
+ * [pymobiledetect](https://pypi.python.org/pypi/pymobiledetect) - Mobile detect python package.
265
+ Made by Bas van Oostveen.
266
+
267
+ **Ruby**
268
+
269
+ * [mobile_detect.rb](https://github.com/ktaragorn/mobile_detect) is a Ruby gem using the JSON data exposed by the
270
+ php project and implementing a basic subset of the API (as much as can be done by the exposed data).
271
+ Made by [Karthik T](https://github.com/ktaragorn).
272
+
273
+ **Go**
274
+
275
+ * [GoMobileDetect](https://github.com/Shaked/gomobiledetect) is a Go port of Mobile Detect class.
276
+ Made by [https://github.com/Shaked](Shaked).
277
+
278
+
279
+ **LUA**
280
+
281
+ * [ua-lua](https://github.com/robinef/ua-lua) is a small lib written in LUA providing device type detection.
282
+ ua-lua is detecting mobile or tablet devices based on user-agent inside nginx daemon.
283
+ Made by [Frédéric Robinet](https://github.com/robinef).
inc/cache/Mobile-Detect-2.8.25/composer.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "mobiledetect/mobiledetectlib",
3
+ "type": "library",
4
+ "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.",
5
+ "keywords": ["mobile", "mobile detect", "mobile detector", "php mobile detect", "detect mobile devices"],
6
+ "homepage": "https://github.com/serbanghita/Mobile-Detect",
7
+ "license": "MIT",
8
+ "authors": [
9
+ {
10
+ "name": "Serban Ghita",
11
+ "email": "serbanghita@gmail.com",
12
+ "homepage": "http://mobiledetect.net",
13
+ "role": "Developer"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.0.0"
18
+ },
19
+ "require-dev": {
20
+ "phpunit/phpunit": "*"
21
+ },
22
+ "autoload": {
23
+ "classmap": ["Mobile_Detect.php"],
24
+ "psr-0": {
25
+ "Detection": "namespaced/"
26
+ }
27
+ }
28
+ }
inc/cache/Mobile-Detect-2.8.25/export/exportToJSON.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Mobile Detect Library
4
+ * - export -
5
+ * =====================
6
+ *
7
+ * Use the resulting JSON export file in other languages
8
+ * other than PHP. Always check for 'version' key because
9
+ * new major versions can modify the structure of the JSON file.
10
+ *
11
+ * The result of running this script is the export.json file.
12
+ *
13
+ * @license Code and contributions have 'MIT License'
14
+ * More details: https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
15
+ *
16
+ */
17
+
18
+ // Included nicejson function to beautify the result JSON file.
19
+ // This library is not mandatory.
20
+ if( file_exists(dirname(__FILE__).'/nicejson/nicejson.php') ) {
21
+ include_once dirname(__FILE__).'/nicejson/nicejson.php';
22
+ }
23
+
24
+ // Include Mobile Detect.
25
+ require_once dirname(__FILE__).'/../Mobile_Detect.php';
26
+ $detect = new Mobile_Detect;
27
+
28
+ $json = array(
29
+ // The current version of Mobile Detect class that
30
+ // is being exported.
31
+ 'version' => $detect->getScriptVersion(),
32
+
33
+ // All headers that trigger 'isMobile' to be 'true',
34
+ // before reaching the User-Agent match detection.
35
+ 'headerMatch' => $detect->getMobileHeaders(),
36
+
37
+ // All possible User-Agent headers.
38
+ 'uaHttpHeaders' => $detect->getUaHttpHeaders(),
39
+
40
+ // All the regexes that trigger 'isMobile' or 'isTablet'
41
+ // to be true.
42
+ 'uaMatch' => array(
43
+ // If match is found, triggers 'isMobile' to be true.
44
+ 'phones' => $detect->getPhoneDevices(),
45
+ // Triggers 'isTablet' to be true.
46
+ 'tablets' => $detect->getTabletDevices(),
47
+ // If match is found, triggers 'isMobile' to be true.
48
+ 'browsers' => $detect->getBrowsers(),
49
+ // If match is found, triggers 'isMobile' to be true.
50
+ 'os' => $detect->getOperatingSystems(),
51
+ // Various utilities. To be further discussed.
52
+ 'utilities' => $detect->getUtilities()
53
+ )
54
+ );
55
+
56
+ $fileName = dirname(__FILE__).'/../Mobile_Detect.json';
57
+ // Write the JSON file to disk.11
58
+ // You can import this file in your app.
59
+ if (file_put_contents(
60
+ $fileName,
61
+ function_exists('json_format') ? json_format($json) : json_encode($json)
62
+ )) {
63
+ echo 'Done. Check '.realpath($fileName).' file.';
64
+ }
65
+ else {
66
+ echo 'Failed to write '.realpath($fileName).' to disk.';
67
+ }
inc/cache/Mobile-Detect-2.8.25/namespaced/Detection/MobileDetect.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Little piece of PHP to make Mobile_Detect auto-loadable in PSR-0 compatible PHP autoloaders like
4
+ * the Symfony Universal ClassLoader by Fabien Potencier. Since PSR-0 handles an underscore in
5
+ * classnames (on the filesystem) as a slash, "Mobile_Detect.php" autoloaders will try to convert
6
+ * the classname and path to "Mobile\Detect.php". This script will ensure autoloading with:
7
+ * - Namespace: Detection
8
+ * - Classname: MobileDetect
9
+ * - Namespased: \Detection\MobileDetect
10
+ * - Autoload path: ./namespaced
11
+ * - Converted path: ./namespaced/Detection/MobileDetect.php
12
+ *
13
+ * Don't forget to use MobileDetect (instead of Mobile_Detect) as class in code when autoloading.
14
+ *
15
+ * Thanks to @WietseWind.
16
+ * For details please check: https://github.com/serbanghita/Mobile-Detect/pull/120
17
+ */
18
+
19
+ namespace Detection;
20
+ require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Mobile_Detect.php';
21
+
22
+ class MobileDetect extends \Mobile_Detect {}
inc/cache/Mobile-Detect-2.8.25/ruleset.xml ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <ruleset name="PSR2">
3
+ <description>The PSR-2 coding standard.</description>
4
+ <arg name="tab-width" value="4"/>
5
+
6
+ <!-- 2. General -->
7
+
8
+ <!-- 2.1 Basic Coding Standard -->
9
+
10
+ <!-- Include the whole PSR-1 standard -->
11
+ <rule ref="PSR1"/>
12
+
13
+ <!-- 2.2 Files -->
14
+
15
+ <!-- All PHP files MUST use the Unix LF (linefeed) line ending. -->
16
+ <rule ref="Generic.Files.LineEndings">
17
+ <properties>
18
+ <property name="eolChar" value="\n"/>
19
+ </properties>
20
+ </rule>
21
+
22
+ <!-- All PHP files MUST end with a single blank line. -->
23
+ <!-- checked in Files/EndFileNewlineSniff -->
24
+
25
+ <!-- The closing ?> tag MUST be omitted from files containing only PHP. -->
26
+ <!-- checked in Files/ClosingTagSniff -->
27
+
28
+ <!-- 2.3 Lines -->
29
+
30
+ <!-- The soft limit on line length MUST be 120 characters; automated style checkers MUST warn but MUST NOT error at the soft limit. -->
31
+ <rule ref="Generic.Files.LineLength">
32
+ <properties>
33
+ <property name="lineLimit" value="0"/>
34
+ <property name="absoluteLineLimit" value="0"/>
35
+ </properties>
36
+ </rule>
37
+
38
+ <!-- There MUST NOT be trailing whitespace at the end of non-blank lines. -->
39
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
40
+ <properties>
41
+ <property name="ignoreBlankLines" value="true"/>
42
+ </properties>
43
+ </rule>
44
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile">
45
+ <severity>0</severity>
46
+ </rule>
47
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EndFile">
48
+ <severity>0</severity>
49
+ </rule>
50
+ <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.EmptyLines">
51
+ <severity>0</severity>
52
+ </rule>
53
+
54
+ <!-- There MUST NOT be more than one statement per line. -->
55
+ <rule ref="Generic.Formatting.DisallowMultipleStatements"/>
56
+
57
+ <!-- 2.4 Indenting -->
58
+
59
+ <!-- Code MUST use an indent of 4 spaces, and MUST NOT use tabs for indenting. -->
60
+ <rule ref="Generic.WhiteSpace.ScopeIndent">
61
+ <properties>
62
+ <property name="ignoreIndentationTokens" type="array" value="T_COMMENT,T_DOC_COMMENT_OPEN_TAG"/>
63
+ </properties>
64
+ </rule>
65
+ <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
66
+
67
+ <!-- 2.5 Keywords and True/False/Null -->
68
+
69
+ <!-- PHP keywords MUST be in lower case. -->
70
+ <rule ref="Generic.PHP.LowerCaseKeyword"/>
71
+
72
+ <!-- The PHP constants true, false, and null MUST be in lower case. -->
73
+ <rule ref="Generic.PHP.LowerCaseConstant"/>
74
+
75
+ <!-- 3. Namespace and Use Declarations -->
76
+
77
+ <!-- When present, there MUST be one blank line after the namespace declaration. -->
78
+ <!-- checked in Namespaces/NamespaceDeclarationSniff -->
79
+
80
+ <!-- When present, all use declarations MUST go after the namespace declaration.
81
+ There MUST be one use keyword per declaration.
82
+ There MUST be one blank line after the use block. -->
83
+ <!-- checked in Namespaces/UseDeclarationSniff -->
84
+
85
+ <!-- 4. Classes, Properties, and Methods -->
86
+
87
+ <!-- 4.1. Extends and Implements -->
88
+
89
+ <!-- The extends and implements keywords MUST be declared on the same line as the class name.
90
+ The opening brace for the class go MUST go on its own line; the closing brace for the class MUST go on the next line after the body.
91
+ Lists of implements MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one interface per line. -->
92
+ <!-- checked in Classes/ClassDeclarationSniff -->
93
+
94
+ <!-- 4.2. Properties -->
95
+
96
+ <!-- Visibility MUST be declared on all properties.
97
+ The var keyword MUST NOT be used to declare a property.
98
+ There MUST NOT be more than one property declared per statement.
99
+ Property names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility. -->
100
+ <!-- checked in Classes/PropertyDeclarationSniff -->
101
+
102
+ <!-- 4.3 Methods -->
103
+
104
+ <!-- Visibility MUST be declared on all methods. -->
105
+ <rule ref="Squiz.Scope.MethodScope"/>
106
+ <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
107
+
108
+ <!-- Method names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility. -->
109
+ <!-- checked in Methods/MethodDeclarationSniff -->
110
+
111
+ <!-- Method names MUST NOT be declared with a space after the method name. The opening brace MUST go on its own line, and the closing brace MUST go on the next line following the body. There MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. -->
112
+ <rule ref="Squiz.Functions.FunctionDeclaration"/>
113
+ <rule ref="Squiz.Functions.LowercaseFunctionKeywords"/>
114
+
115
+ <!-- 4.4 Method Arguments -->
116
+
117
+ <!-- In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma. -->
118
+ <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
119
+ <properties>
120
+ <property name="equalsSpacing" value="1"/>
121
+ </properties>
122
+ </rule>
123
+ <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint">
124
+ <severity>0</severity>
125
+ </rule>
126
+
127
+ <!-- Method arguments with default values MUST go at the end of the argument list. -->
128
+ <rule ref="PEAR.Functions.ValidDefaultValue"/>
129
+
130
+ <!-- Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. When the argument list is split across multiple lines, the closing parenthesis and opening brace MUST be placed together on their own line with one space between them. -->
131
+ <rule ref="Squiz.Functions.MultiLineFunctionDeclaration"/>
132
+
133
+ <!-- 4.5 abstract, final, and static -->
134
+
135
+ <!-- When present, the abstract and final declarations MUST precede the visibility declaration.
136
+ When present, the static declaration MUST come after the visibility declaration. -->
137
+ <!-- checked in Methods/MethodDeclarationSniff -->
138
+
139
+ <!-- 4.6 Method and Function Calls -->
140
+
141
+ <!-- When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis, there MUST NOT be a space after the opening parenthesis, and there MUST NOT be a space before the closing parenthesis. In the argument list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.
142
+ Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line. -->
143
+ <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
144
+ <rule ref="PSR2.Methods.FunctionCallSignature.SpaceAfterCloseBracket">
145
+ <severity>0</severity>
146
+ </rule>
147
+
148
+ <!-- 5. Control Structures -->
149
+
150
+ <!-- The general style rules for control structures are as follows:
151
+ There MUST be one space after the control structure keyword
152
+ There MUST NOT be a space after the opening parenthesis
153
+ There MUST NOT be a space before the closing parenthesis
154
+ There MUST be one space between the closing parenthesis and the opening brace
155
+ The structure body MUST be indented once
156
+ The closing brace MUST be on the next line after the body -->
157
+ <rule ref="Squiz.ControlStructures.ControlSignature"/>
158
+ <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"/>
159
+ <rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
160
+ <rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
161
+ <rule ref="Squiz.ControlStructures.LowercaseDeclaration"/>
162
+ <!-- checked in ControlStructures/ControlStructureSpacingSniff -->
163
+
164
+ <!-- The body of each structure MUST be enclosed by braces. This standardizes how the structures look, and reduces the likelihood of introducing errors as new lines get added to the body. -->
165
+ <rule ref="Generic.ControlStructures.InlineControlStructure"/>
166
+
167
+ <!-- 5.1. if, elseif, else -->
168
+
169
+ <!-- The keyword elseif SHOULD be used instead of else if so that all control keywords look like single words. -->
170
+ <!-- checked in ControlStructures/ElseIfDeclarationSniff -->
171
+
172
+ <!-- 5.2. switch, case -->
173
+
174
+ <!-- The case statement MUST be indented once from switch, and the break keyword (or other terminating keyword) MUST be indented at the same level as the case body. There MUST be a comment such as // no break when fall-through is intentional in a non-empty case body. -->
175
+ <!-- checked in ControlStructures/SwitchDeclarationSniff -->
176
+
177
+ <!-- 6. Closures -->
178
+
179
+ <!-- Closures MUST be declared with a space after the function keyword, and a space before and after the use keyword.
180
+ The opening brace MUST go on the same line, and the closing brace MUST go on the next line following the body.
181
+ There MUST NOT be a space after the opening parenthesis of the argument list or variable list, and there MUST NOT be a space before the closing parenthesis of the argument list or variable list.
182
+ In the argument list and variable list, there MUST NOT be a space before each comma, and there MUST be one space after each comma.
183
+ Closure arguments with default values MUST go at the end of the argument list.
184
+ Argument lists and variable lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument or variable per line.
185
+ When the ending list (whether or arguments or variables) is split across multiple lines, the closing parenthesis and opening brace MUST be placed together on their own line with one space between them. -->
186
+ <!-- checked in Squiz.Functions.MultiLineFunctionDeclaration -->
187
+ </ruleset>
inc/cache/config-cache.php ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ //Based on some work of simple-cache
22
+ if ( ! defined( 'ABSPATH' ) ) exit;
23
+
24
+ class Breeze_ConfigCache {
25
+
26
+ /*
27
+ * Create advanced-cache file
28
+ */
29
+ public function write(){
30
+ global $wp_filesystem;
31
+
32
+ $file = untrailingslashit( WP_CONTENT_DIR ) . '/advanced-cache.php';
33
+
34
+ $config = get_option('breeze_basic_settings');
35
+
36
+ $file_string = '';
37
+
38
+ if (!empty($config) && !empty( $config['breeze-active'] ) ) {
39
+
40
+ $file_string = '<?php ' .
41
+ "\n\r" . "defined( 'ABSPATH' ) || exit;" .
42
+ "\n\r" . "define( 'BREEZE_ADVANCED_CACHE', true );" .
43
+ "\n\r" . 'if ( is_admin() ) { return; }' .
44
+ "\n\r" . "if ( ! @file_exists( '" . BREEZE_PLUGIN_DIR . "breeze.php' ) ) { return; }" .
45
+ "\n\r" . "if ( ! @file_exists( '". WP_CONTENT_DIR . "/breeze-config/breeze-config.php' ) ) { return; }" .
46
+ "\n\r" . "\$GLOBALS['breeze_config'] = include('". WP_CONTENT_DIR . "/breeze-config/breeze-config.php' );" .
47
+ "\n\r" . "if ( empty( \$GLOBALS['breeze_config'] ) || empty( \$GLOBALS['breeze_config']['cache_options']['breeze-active'] ) ) { return; }" .
48
+ "\n\r" . "if ( @file_exists( '". BREEZE_PLUGIN_DIR . "inc/cache/execute-cache.php' ) ) { include_once( '". BREEZE_PLUGIN_DIR . "inc/cache/execute-cache.php' ); }" . "\n\r";
49
+
50
+ }
51
+
52
+ if ( ! $wp_filesystem->put_contents( $file, $file_string ) ) {
53
+ return false;
54
+ }
55
+
56
+ return true;
57
+ }
58
+
59
+ /**
60
+ * Function write parameter to breeze-config
61
+ * @return breeze_Cache
62
+ */
63
+ public static function write_config_cache(){
64
+ $settings = get_option('breeze_basic_settings');
65
+ $config = get_option('breeze_advanced_settings');
66
+
67
+ $storage = array(
68
+ 'homepage' => get_site_url(),
69
+ 'cache_options' => $settings,
70
+ 'disable_per_adminuser' => 0,
71
+ 'exclude_url' => array(),
72
+ );
73
+
74
+ if(!empty($settings['breeze-disable-admin'])){
75
+ $storage['disable_per_adminuser'] = $settings['breeze-disable-admin'];
76
+ }
77
+ if(!empty($config['breeze-exclude-urls'])){
78
+ $storage['exclude_url'] = $config['breeze-exclude-urls'];
79
+ }
80
+
81
+ if(! self::write_config($storage)){
82
+ return false;
83
+ }
84
+ return true;
85
+ }
86
+
87
+ /*
88
+ * create file config storage parameter used for cache
89
+ */
90
+ public static function write_config( $config ) {
91
+
92
+ global $wp_filesystem;
93
+
94
+ $config_dir = WP_CONTENT_DIR . '/breeze-config';
95
+
96
+ $site_url_parts = parse_url( site_url() );
97
+
98
+ $config_file = $config_dir . '/breeze-config.php';
99
+
100
+ $wp_filesystem->mkdir( $config_dir );
101
+
102
+ $config_file_string = '<?php ' . "\n\r" . "defined( 'ABSPATH' ) || exit;" . "\n\r" . 'return ' . var_export( $config, true ) . '; ' . "\n\r";
103
+ if ( ! $wp_filesystem->put_contents( $config_file, $config_file_string ) ) {
104
+ return false;
105
+ }
106
+
107
+ return true;
108
+ }
109
+ //turn on / of wp cahe
110
+ public function toggle_caching( $status ) {
111
+
112
+ global $wp_filesystem;
113
+ if ( defined( 'WP_CACHE' ) && WP_CACHE === $status ) {
114
+ return;
115
+ }
116
+
117
+ // Lets look 4 levels deep for wp-config.php
118
+ $levels = 4;
119
+
120
+ $file = '/wp-config.php';
121
+ $config_path = false;
122
+
123
+ for ( $i = 1; $i <= 3; $i++ ) {
124
+ if ( $i > 1 ) {
125
+ $file = '/..' . $file;
126
+ }
127
+
128
+ if ( $wp_filesystem->exists( untrailingslashit( ABSPATH ) . $file ) ) {
129
+ $config_path = untrailingslashit( ABSPATH ) . $file;
130
+ break;
131
+ }
132
+ }
133
+
134
+ // Couldn't find wp-config.php
135
+ if ( ! $config_path ) {
136
+ return false;
137
+ }
138
+
139
+ $config_file_string = $wp_filesystem->get_contents( $config_path );
140
+
141
+ // Config file is empty. Maybe couldn't read it?
142
+ if ( empty( $config_file_string ) ) {
143
+ return false;
144
+ }
145
+
146
+ $config_file = preg_split( "#(\n|\r)#", $config_file_string );
147
+ $line_key = false;
148
+
149
+ foreach ( $config_file as $key => $line ) {
150
+ if ( ! preg_match( '/^\s*define\(\s*(\'|")([A-Z_]+)(\'|")(.*)/', $line, $match ) ) {
151
+ continue;
152
+ }
153
+
154
+ if ( $match[2] == 'WP_CACHE' ) {
155
+ $line_key = $key;
156
+ }
157
+ }
158
+
159
+ if ( $line_key !== false ) {
160
+ unset( $config_file[ $line_key ] );
161
+ }
162
+
163
+ $status_string = ( $status ) ? 'true' : 'false';
164
+
165
+ array_shift( $config_file );
166
+ array_unshift( $config_file, '<?php', "define( 'WP_CACHE', $status_string ); " );
167
+
168
+ foreach ( $config_file as $key => $line ) {
169
+ if ( '' === $line ) {
170
+ unset( $config_file[$key] );
171
+ }
172
+ }
173
+
174
+ if ( ! $wp_filesystem->put_contents( $config_path, implode( PHP_EOL, $config_file ) ) ) {
175
+ return false;
176
+ }
177
+
178
+ return true;
179
+ }
180
+ //delete file for clean up
181
+
182
+ public function clean_up() {
183
+
184
+ global $wp_filesystem;
185
+ $file = untrailingslashit( WP_CONTENT_DIR ) . '/advanced-cache.php';
186
+
187
+ $ret = true;
188
+
189
+ if ( ! $wp_filesystem->delete( $file ) ) {
190
+ $ret = false;
191
+ }
192
+
193
+ $folder = untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze';
194
+
195
+ if ( ! $wp_filesystem->delete( $folder, true ) ) {
196
+ $ret = false;
197
+ }
198
+
199
+ $folder = untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze-minification';
200
+
201
+ if ( ! $wp_filesystem->delete( $folder, true ) ) {
202
+ $ret = false;
203
+ }
204
+
205
+ return $ret;
206
+ }
207
+
208
+ //delete config file
209
+ public function clean_config() {
210
+
211
+ global $wp_filesystem;
212
+
213
+ $folder = untrailingslashit( WP_CONTENT_DIR ) . '/breeze-config';
214
+ if ( ! $wp_filesystem->delete( $folder, true ) ) {
215
+ return false;
216
+ }
217
+
218
+ return true;
219
+ }
220
+
221
+
222
+ public static function factory() {
223
+
224
+ static $instance;
225
+
226
+ if ( ! $instance ) {
227
+ $instance = new self();
228
+ }
229
+
230
+ return $instance;
231
+ }
232
+ }
inc/cache/execute-cache.php ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Based on some work of https://github.com/tlovett1/simple-cache/blob/master/inc/dropins/file-based-page-cache.php
4
+ */
5
+ defined('ABSPATH') || exit;
6
+ // Include and instantiate the class.
7
+
8
+ require_once 'Mobile-Detect-2.8.25/Mobile_Detect.php';
9
+ $detect = new \Cloudways\Breeze\Mobile_Detect\Mobile_Detect;
10
+
11
+ // Don't cache robots.txt or htacesss
12
+ if (strpos($_SERVER['REQUEST_URI'], 'robots.txt') !== false || strpos($_SERVER['REQUEST_URI'], '.htaccess') !== false) {
13
+ return;
14
+ }
15
+
16
+ // Don't cache non-GET requests
17
+ if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'GET') {
18
+ return;
19
+ }
20
+
21
+ $file_extension = $_SERVER['REQUEST_URI'];
22
+ $file_extension = preg_replace('#^(.*?)\?.*$#', '$1', $file_extension);
23
+ $file_extension = trim(preg_replace('#^.*\.(.*)$#', '$1', $file_extension));
24
+
25
+ // Don't cache disallowed extensions. Prevents wp-cron.php, xmlrpc.php, etc.
26
+ if (!preg_match('#index\.php$#i', $_SERVER['REQUEST_URI']) && in_array($file_extension, array('php', 'xml', 'xsl'))) {
27
+ return;
28
+ }
29
+
30
+ $url_path = breeze_get_url_path();
31
+ $user_logged = false;
32
+ $filename = $url_path . 'guest';
33
+ // Don't cache
34
+ if (!empty($_COOKIE)) {
35
+ $wp_cookies = array('wordpressuser_', 'wordpresspass_', 'wordpress_sec_', 'wordpress_logged_in_');
36
+
37
+ foreach ($_COOKIE as $key => $value) {
38
+ // Logged in!
39
+ if (strpos($key, 'wordpress_logged_in_') !== false) {
40
+ $user_logged = true;
41
+ }
42
+
43
+ }
44
+
45
+ if ($user_logged) {
46
+ foreach ($_COOKIE as $k => $v) {
47
+ if (strpos($k, 'wordpress_logged_in_') !== false) {
48
+ $nameuser = substr($v, 0, strpos($v, '|'));
49
+ $filename = $url_path . strtolower($nameuser);
50
+ }
51
+ }
52
+ }
53
+ if (!empty($_COOKIE['breeze_commented_posts'])) {
54
+ foreach ($_COOKIE['breeze_commented_posts'] as $path) {
55
+ if (rtrim($path, '/') === rtrim($_SERVER['REQUEST_URI'], '/')) {
56
+ // User commented on this post
57
+ return;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ //check disable cache for page
64
+ $domain = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'];
65
+ //decode url with russian language
66
+ $current_url = $domain . rawurldecode($_SERVER['REQUEST_URI']);
67
+ $check_url = array();
68
+ $opts_config = $GLOBALS['breeze_config'];
69
+ if (!empty($opts_config['exclude_url'])) {
70
+ foreach ($opts_config['exclude_url'] as $v) {
71
+ if ($v && $v[0] == '/') {
72
+ //part url
73
+ if (preg_match('[\*]', $v) == 1) {
74
+ $v = substr($v, 0, strpos($v, "/(.*)"));
75
+
76
+ if (!empty($v)) {
77
+ if (strpos($current_url, $v) != false) {
78
+ $check_url[] = $current_url;
79
+ }
80
+ }
81
+
82
+ } else {
83
+ if (!empty($v)) {
84
+ if (strpos($current_url, $v) != false) {
85
+ $url = $current_url;
86
+ } else {
87
+ $homepage = $opts_config['homepage'];
88
+ $url = $homepage . $v;
89
+ }
90
+ }
91
+
92
+ $check_url[] = $url;
93
+ }
94
+ } else {
95
+ if (preg_match('[\*]', $v) == 1) {
96
+ $v = substr($v, 0, strpos($v, "&(.*)"));
97
+ $v = substr($v, -10);
98
+ if (!empty($v)) {
99
+ if (strpos($current_url, $v) != false) {
100
+ $check_url[] = $current_url;
101
+ }
102
+ }
103
+ } else {
104
+ //full url
105
+ $check_url[] = trim($v);
106
+ }
107
+ }
108
+ }
109
+ }
110
+ //load cache
111
+ if (!in_array($current_url, $check_url)) {
112
+ $devices = $opts_config['cache_options'];
113
+ $X1 = '';
114
+ // Detect devices
115
+ if ($detect->isMobile() && !$detect->isTablet()) {
116
+ // The first X will be D for Desktop cache
117
+ // M for Mobile cache
118
+ // T for Tablet cache
119
+ if ((int)$devices['breeze-mobile-cache'] == 1) {
120
+ $X1 = 'D';
121
+ $filename .= '_breeze_cache_desktop';
122
+ }
123
+ if ((int)$devices['breeze-mobile-cache'] == 2) {
124
+ $X1 = 'M';
125
+ $filename .= '_breeze_cache_mobile';
126
+ }
127
+
128
+ } else {
129
+ if ((int)$devices['breeze-desktop-cache'] == 1) {
130
+ $X1 = 'D';
131
+ $filename .= '_breeze_cache_desktop';
132
+ }
133
+ }
134
+
135
+ breeze_serve_cache($filename, $url_path, $X1,$devices);
136
+ ob_start('breeze_cache');
137
+
138
+ }
139
+
140
+ /**
141
+ * Cache output before it goes to the browser
142
+ *
143
+ * @param string $buffer
144
+ * @param int $flags
145
+ * @since 1.0
146
+ * @return string
147
+ */
148
+ function breeze_cache($buffer, $flags)
149
+ {
150
+
151
+ require_once 'Mobile-Detect-2.8.25/Mobile_Detect.php';
152
+ $detect = new \Cloudways\Breeze\Mobile_Detect\Mobile_Detect;
153
+ //not cache per administrator if option disable optimization for admin users clicked
154
+ if (!empty($GLOBALS['breeze_config']) && (int)$GLOBALS['breeze_config']['disable_per_adminuser']) {
155
+ $current_user = wp_get_current_user();
156
+ if (in_array('administrator', $current_user->roles)) {
157
+ return $buffer;
158
+ }
159
+ }
160
+
161
+ if (strlen($buffer) < 255) {
162
+ return $buffer;
163
+ }
164
+
165
+ // Don't cache search, 404, or password protected
166
+ if (is_404() || is_search() || post_password_required()) {
167
+ return $buffer;
168
+ }
169
+ global $wp_filesystem;
170
+ if (empty($wp_filesystem)) {
171
+ require_once(ABSPATH . '/wp-admin/includes/file.php');
172
+ WP_Filesystem();
173
+ }
174
+ $url_path = breeze_get_url_path();
175
+
176
+ // Make sure we can read/write files and that proper folders exist
177
+ if (!$wp_filesystem->exists(untrailingslashit(WP_CONTENT_DIR) . '/cache')) {
178
+ if (!$wp_filesystem->mkdir(untrailingslashit(WP_CONTENT_DIR) . '/cache')) {
179
+ // Can not cache!
180
+ return $buffer;
181
+ }
182
+ }
183
+
184
+ if (!$wp_filesystem->exists(untrailingslashit(WP_CONTENT_DIR) . '/cache/breeze')) {
185
+ if (!$wp_filesystem->mkdir(untrailingslashit(WP_CONTENT_DIR) . '/cache/breeze')) {
186
+ // Can not cache!
187
+ return $buffer;
188
+ }
189
+ }
190
+
191
+ if (!$wp_filesystem->exists(untrailingslashit(WP_CONTENT_DIR) . '/cache/breeze/' . md5($url_path))) {
192
+ if (!$wp_filesystem->mkdir(untrailingslashit(WP_CONTENT_DIR) . '/cache/breeze/' . md5($url_path))) {
193
+ // Can not cache!
194
+ return $buffer;
195
+ }
196
+ }
197
+
198
+ $path = untrailingslashit(WP_CONTENT_DIR) . '/cache/breeze/' . md5($url_path) . '/';
199
+
200
+ $modified_time = time(); // Make sure modified time is consistent
201
+
202
+ if (preg_match('#</html>#i', $buffer)) {
203
+ $buffer .= "\n<!-- Cache served by breeze CACHE - Last modified: " . gmdate('D, d M Y H:i:s', $modified_time) . " GMT -->\n";
204
+ }
205
+ $headers = array(
206
+ array(
207
+ 'name' => 'Content-Length',
208
+ 'value' => strlen($buffer)
209
+ ),
210
+ array(
211
+ 'name' => 'Content-Type',
212
+ 'value' => 'text/html; charset=utf-8'
213
+ ),
214
+ array(
215
+ 'name' => 'Last-Modified',
216
+ 'value' => gmdate('D, d M Y H:i:s', $modified_time) . ' GMT'
217
+ )
218
+ );
219
+
220
+ if(!isset($_SERVER['HTTP_X_VARNISH'])) {
221
+ $headers = array_merge(array(
222
+ array(
223
+ 'name' => 'Expires',
224
+ 'value' => 'Wed, 17 Aug 2005 00:00:00 GMT'
225
+ ),
226
+ array(
227
+ 'name' => 'Cache-Control',
228
+ 'value' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
229
+ ),
230
+ array(
231
+ 'name' => 'Pragma',
232
+ 'value' => 'no-cache'
233
+ )
234
+ ));
235
+ }
236
+
237
+ $data = serialize(array('body' => $buffer, 'headers' => $headers));
238
+ //cache per users
239
+ if (is_user_logged_in()) {
240
+ $current_user = wp_get_current_user();
241
+ if ($current_user->user_login) {
242
+ $url_path .= $current_user->user_login;
243
+ }
244
+ } else {
245
+ $url_path .= 'guest';
246
+ }
247
+ $devices = $GLOBALS['breeze_config']['cache_options'];
248
+ // Detect devices
249
+ if ($detect->isMobile() && !$detect->isTablet()) {
250
+ if ($devices['breeze-mobile-cache'] == 1) {
251
+ $X1 = 'D';
252
+ $url_path .= '_breeze_cache_desktop';
253
+ }
254
+ if ($devices['breeze-mobile-cache'] == 2) {
255
+ $X1 = 'M';
256
+ $url_path .= '_breeze_cache_mobile';
257
+ }
258
+ } else {
259
+ if ($devices['breeze-desktop-cache'] == 1) {
260
+ $X1 = 'D';
261
+ $url_path .= '_breeze_cache_desktop';
262
+ }
263
+ }
264
+
265
+ if (strpos($url_path, '_breeze_cache_') !== false) {
266
+ if (!empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression']) && function_exists('gzencode')) {
267
+ $wp_filesystem->put_contents($path . md5($url_path . '/index.gzip.html') . '.php', $data);
268
+ $wp_filesystem->touch($path . md5($url_path . '/index.gzip.html') . '.php', $modified_time);
269
+ } else {
270
+ $wp_filesystem->put_contents($path . md5($url_path . '/index.html') . '.php', $data);
271
+ $wp_filesystem->touch($path . md5($url_path . '/index.html') . '.php', $modified_time);
272
+ }
273
+ } else {
274
+ return $buffer;
275
+ }
276
+ //set cache provider header if not exists cache file
277
+ header('Cache-Provider:CLOUDWAYS-CACHE-' . $X1 . 'C');
278
+
279
+ // Do not send this header in case we are behind a varnish proxy
280
+ if(!isset($_SERVER['HTTP_X_VARNISH'])) {
281
+ header('Cache-Control: no-cache'); // Check back every time to see if re-download is necessary
282
+ }
283
+
284
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT');
285
+
286
+ if (function_exists('ob_gzhandler') && !empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression'])) {
287
+ $ini_output_compression = ini_get('zlib.output_compression');
288
+ $array_values = array('1', 'On', 'on');
289
+ if (in_array($ini_output_compression, $array_values)) {
290
+ return $buffer;
291
+ } else {
292
+ return ob_gzhandler($buffer, $flags);
293
+ }
294
+ } else {
295
+ return $buffer;
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Get URL path for caching
301
+ *
302
+ * @since 1.0
303
+ * @return string
304
+ */
305
+ function breeze_get_url_path()
306
+ {
307
+
308
+ $host = (isset($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : '';
309
+
310
+ return "http://" . rtrim($host, '/') . $_SERVER['REQUEST_URI'];
311
+ }
312
+
313
+ /**
314
+ * Optionally serve cache and exit
315
+ *
316
+ * @since 1.0
317
+ */
318
+ function breeze_serve_cache($filename, $url_path, $X1,$opts)
319
+ {
320
+ if (strpos($filename, '_breeze_cache_') === false) {
321
+ return;
322
+ }
323
+
324
+ if (function_exists('gzencode') && !empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression'])) {
325
+ $file_name = md5($filename . '/index.gzip.html') . '.php';
326
+ } else {
327
+ $file_name = md5($filename . '/index.html') . '.php';
328
+ }
329
+
330
+
331
+ $path = rtrim(WP_CONTENT_DIR, '/') . '/cache/breeze/' . md5($url_path) . '/' . $file_name;
332
+
333
+ $modified_time = (int)@filemtime($path);
334
+
335
+ if (!empty($opts['breeze-browser-cache']) &&!empty($modified_time) && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modified_time) {
336
+ header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304);
337
+ exit;
338
+ }
339
+
340
+ if (@file_exists($path)) {
341
+
342
+ $cacheFile = file_get_contents($path);
343
+
344
+
345
+ if ($cacheFile != false) {
346
+ $datas = unserialize($cacheFile);
347
+ foreach ($datas['headers'] as $data) {
348
+ header($data['name'] . ': ' . $data['value']);
349
+ }
350
+ //set cache provider header
351
+ header('Cache-Provider:CLOUDWAYS-CACHE-' . $X1 . 'E');
352
+
353
+ $client_support_gzip = true;
354
+
355
+ //check gzip request from client
356
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false || strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') === false)) {
357
+ $client_support_gzip = false;
358
+ }
359
+
360
+ if ($client_support_gzip && function_exists('gzdecode') && !empty($GLOBALS['breeze_config']['cache_options']['breeze-gzip-compression'])) {
361
+ //if file is zip
362
+
363
+ $content = gzencode($datas['body'], 9);
364
+ header('Content-Encoding: gzip');
365
+ header("cache-control: must-revalidate");
366
+ header('Content-Length: ' . strlen($content));
367
+ header('Vary: Accept-Encoding');
368
+ echo $content;
369
+ } else {
370
+ //render page cache
371
+ echo $datas['body'];
372
+ }
373
+ exit;
374
+ }
375
+ }
376
+ }
377
+
inc/cache/purge-cache.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ if ( ! defined( 'ABSPATH' ) ) exit;
22
+ class Breeze_PurgeCache {
23
+
24
+ public function set_action() {
25
+ add_action('pre_post_update', array($this, 'purge_post_on_update'), 10, 1);
26
+ add_action('save_post', array($this, 'purge_post_on_update'), 10, 1);
27
+ add_action('wp_trash_post', array($this, 'purge_post_on_update'), 10, 1);
28
+ add_action('comment_post', array($this, 'purge_post_on_new_comment'), 10, 3);
29
+ add_action('wp_set_comment_status', array($this, 'purge_post_on_comment_status_change'), 10, 2);
30
+ add_action('set_comment_cookies', array($this, 'set_comment_cookie_exceptions'), 10, 2);
31
+ }
32
+
33
+ /**
34
+ * When user posts a comment, set a cookie so we don't show them page cache
35
+ *
36
+ * @param WP_Comment $comment
37
+ * @param WP_User $user
38
+ * @since 1.3
39
+ */
40
+ public function set_comment_cookie_exceptions($comment, $user) {
41
+ $config = get_option('breeze_basic_settings');
42
+ // File based caching only
43
+ if (!empty($config['breeze-active'])) {
44
+
45
+ $post_id = $comment->comment_post_ID;
46
+
47
+ setcookie('breeze_commented_posts[' . $post_id . ']', parse_url(get_permalink($post_id), PHP_URL_PATH), ( time() + HOUR_IN_SECONDS * 24 * 30));
48
+ }
49
+ }
50
+
51
+ // Automatically purge all file based page cache on post changes
52
+ public function purge_post_on_update($post_id) {
53
+ $post_type = get_post_type($post_id);
54
+ if (( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) || 'revision' === $post_type) {
55
+ return;
56
+ } elseif (!current_user_can('edit_post', $post_id) && (!defined('DOING_CRON') || !DOING_CRON )) {
57
+ return;
58
+ }
59
+
60
+ $config = get_option('breeze_basic_settings');
61
+
62
+ // File based caching only
63
+ if (!empty($config['breeze-active'])) {
64
+ self::breeze_cache_flush();
65
+ }
66
+ }
67
+
68
+ public function purge_post_on_new_comment($comment_ID, $approved, $commentdata) {
69
+ if (empty($approved)) {
70
+ return;
71
+ }
72
+ $config = get_option('breeze_basic_settings');
73
+ // File based caching only
74
+ if (!empty($config['breeze-active'])) {
75
+ $post_id = $commentdata['comment_post_ID'];
76
+
77
+ global $wp_filesystem;
78
+
79
+ if ( empty( $wp_filesystem ) ) {
80
+ require_once( ABSPATH . '/wp-admin/includes/file.php' );
81
+ WP_Filesystem();
82
+ }
83
+
84
+ $url_path = get_permalink($post_id);
85
+ if ( $wp_filesystem->exists( untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze/' . md5($url_path) ) ) {
86
+ $wp_filesystem->rmdir( untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze/' . md5($url_path), true );
87
+ }
88
+ }
89
+ }
90
+
91
+ // if a comments status changes, purge it's parent posts cache
92
+ public function purge_post_on_comment_status_change($comment_ID, $comment_status) {
93
+ $config = get_option('breeze_basic_settings');
94
+
95
+ // File based caching only
96
+ if (!empty($config['breeze-active'])) {
97
+ $comment = get_comment($comment_ID);
98
+ if(!empty($comment)){
99
+ $post_id = $comment->comment_post_ID;
100
+
101
+ global $wp_filesystem;
102
+
103
+ WP_Filesystem();
104
+
105
+ $url_path = get_permalink($post_id);
106
+
107
+ if ( $wp_filesystem->exists( untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze/' . md5($url_path) ) ) {
108
+ $wp_filesystem->rmdir( untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze/' . md5($url_path), true );
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ //clean cache
115
+ public static function breeze_cache_flush() {
116
+ global $wp_filesystem;
117
+
118
+ require_once( ABSPATH . 'wp-admin/includes/file.php');
119
+
120
+ WP_Filesystem();
121
+
122
+ $wp_filesystem->rmdir( untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze', true );
123
+
124
+ if ( function_exists( 'wp_cache_flush' ) ) {
125
+ wp_cache_flush();
126
+ }
127
+ }
128
+
129
+ //delete file for clean up
130
+
131
+ public function clean_up() {
132
+
133
+ global $wp_filesystem;
134
+ $file = untrailingslashit( WP_CONTENT_DIR ) . '/advanced-cache.php';
135
+
136
+ $ret = true;
137
+
138
+ if ( ! $wp_filesystem->delete( $file ) ) {
139
+ $ret = false;
140
+ }
141
+
142
+ $folder = untrailingslashit( WP_CONTENT_DIR ) . '/cache/breeze';
143
+
144
+ if ( ! $wp_filesystem->delete( $folder, true ) ) {
145
+ $ret = false;
146
+ }
147
+
148
+ return $ret;
149
+ }
150
+
151
+ /**
152
+ * Return an instance of the current class, create one if it doesn't exist
153
+ * @since 1.0
154
+ * @return object
155
+ */
156
+ public static function factory() {
157
+
158
+ static $instance;
159
+
160
+ if (!$instance) {
161
+ $instance = new self();
162
+ $instance->set_action();
163
+ }
164
+
165
+ return $instance;
166
+ }
167
+
168
+ }
169
+ $settings = get_option('breeze_basic_settings');
170
+ if($settings['breeze-active']){
171
+ Breeze_PurgeCache::factory();
172
+ }
inc/cache/purge-per-time.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_PurgeCacheTime {
24
+ protected $timettl = false;
25
+ protected $normalcache = 0;
26
+ protected $varnishcache = 0;
27
+ public function __construct($settings = null)
28
+ {
29
+ $this->timettl = $settings['breeze-ttl'];
30
+
31
+ $this->normalcache = (int)$settings['breeze-active'];
32
+
33
+ $this->varnishcache = (int)$settings['breeze-varnish-purge'];
34
+
35
+ add_action( 'breeze_purge_cache', array( $this, 'schedule_varnish' ) );
36
+ add_action( 'init', array( $this, 'schedule_events' ) );
37
+ add_filter( 'cron_schedules', array( $this, 'filter_cron_schedules' ) );
38
+
39
+ }
40
+ // * Unschedule events
41
+ public function unschedule_events() {
42
+ $timestamp = wp_next_scheduled( 'breeze_purge_cache' );
43
+
44
+ wp_unschedule_event( $timestamp, 'breeze_purge_cache' );
45
+ }
46
+ // set up schedule_events
47
+ public function schedule_events() {
48
+
49
+ $timestamp = wp_next_scheduled( 'breeze_purge_cache' );
50
+
51
+ // Expire cache never
52
+ if ( isset( $this->timettl ) && (int)$this->timettl === 0 ) {
53
+ wp_unschedule_event( $timestamp, 'breeze_purge_cache' );
54
+ return;
55
+ }
56
+
57
+ if ( ! $timestamp ) {
58
+ wp_schedule_event( time(), 'breeze_varnish_time', 'breeze_purge_cache' );
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Add custom cron schedule
64
+ */
65
+ public function filter_cron_schedules( $schedules ) {
66
+ if ( !empty( $this->timettl ) && is_numeric($this->timettl) && (int)$this->timettl > 0 ) {
67
+ $interval = $this->timettl * 60;
68
+ } else {
69
+ $interval = '86400'; // One day
70
+ }
71
+
72
+ $schedules['breeze_varnish_time'] = array(
73
+ 'interval' => apply_filters( 'breeze_varnish_purge_interval', $interval ),
74
+ 'display' => esc_html__( 'Cloudways Varnish Purge Interval', 'breeze' ),
75
+ );
76
+
77
+ return $schedules;
78
+ }
79
+
80
+ //execute purge varnish after time life
81
+ public function schedule_varnish(){
82
+ // Purge varnish cache
83
+ if($this->varnishcache){
84
+ $homepage = home_url().'/?breeze';
85
+ $main = new Breeze_PurgeVarnish();
86
+ $main->purge_cache($homepage);
87
+ }
88
+ // Purge normal cache
89
+ if($this->normalcache){
90
+ Breeze_PurgeCache::breeze_cache_flush();
91
+ }
92
+
93
+ }
94
+
95
+ public static function factory() {
96
+ static $instance;
97
+ if ( ! $instance ) {
98
+ $instance = new self();
99
+ }
100
+ return $instance;
101
+ }
102
+ }
103
+
104
+ $basic = get_option('breeze_basic_settings');
105
+ $varnish = get_option('breeze_varnish_cache');
106
+ //Enabled auto purge the varnish caching by time life
107
+ $params = array(
108
+ 'breeze-active' => (isset($basic['breeze-active'])?(int)$basic['breeze-active']:0),
109
+ 'breeze-ttl' => (isset($basic['breeze-ttl'])?(int)$basic['breeze-ttl']:0),
110
+ 'breeze-varnish-purge' => (isset($varnish['auto-purge-varnish'])?(int)$varnish['auto-purge-varnish']:0),
111
+ );
112
+
113
+ if($params['breeze-active'] || $params['breeze-varnish-purge']){
114
+ $purgeTime = new Breeze_PurgeCacheTime($params);
115
+ }
116
+
inc/cache/purge-varnish.php ADDED
@@ -0,0 +1,338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_PurgeVarnish {
24
+ protected $blogId;
25
+
26
+ protected $urlsPurge = array();
27
+
28
+ protected $autoPurge = false;
29
+
30
+ protected $actions = array(
31
+ 'switch_theme', // After a theme is changed
32
+ 'save_post', // Save a post
33
+ 'deleted_post', // Delete a post
34
+ 'trashed_post', // Empty Trashed post
35
+ 'edit_post', // Edit a post - includes leaving comments
36
+ 'delete_attachment', // Delete an attachment - includes re-uploading
37
+ );
38
+
39
+ protected $actionsNoId = array('switch_theme');
40
+
41
+ public function __construct(){
42
+ global $blog_id;
43
+ $this->blogId = $blog_id;
44
+ $settings = get_option('breeze_varnish_cache');
45
+ //storage config
46
+ if(!empty($settings['auto-purge-varnish'])) $this->autoPurge = (int)$settings['auto-purge-varnish'];
47
+
48
+ add_action('init', array($this, 'init'));
49
+ }
50
+
51
+ public function init()
52
+ {
53
+ //Push urlsPurge
54
+ if($this->autoPurge){
55
+ if (!empty($this->actions)) {
56
+ //if enabled auto purge option , this action will start
57
+ foreach ($this->actions as $action) {
58
+ if (in_array($action, $this->actionsNoId)) {
59
+ add_action($action, function () {
60
+ array_push($this->urlsPurge, home_url() . '/?breeze');
61
+ });
62
+ } else {
63
+ add_action($action, array($this, 'purge_post'));
64
+ }
65
+ }
66
+ }
67
+
68
+ //Pust urlsPurge after comment
69
+ add_action('comment_post',array($this,'purge_post_on_comment'),10,3);
70
+ add_action('wp_set_comment_status', array($this, 'purge_post_on_comment_status'), 10, 2);
71
+ }
72
+
73
+ //Execute Purge
74
+ add_action('shutdown', array($this, 'breeze_execute_purge'));
75
+
76
+ }
77
+
78
+ /**
79
+ * Execute Purge
80
+ *
81
+ */
82
+ public function breeze_execute_purge()
83
+ {
84
+ $urlsPurge = array_unique($this->urlsPurge);
85
+
86
+ if (!empty($urlsPurge)) {
87
+ foreach ($urlsPurge as $url) {
88
+ $this->purge_cache($url);
89
+ }
90
+ } else {
91
+ $homepage = home_url().'/?breeze';
92
+ if (isset($_REQUEST['breeze_action']) && $_REQUEST['breeze_action'] == 'breeze_settings') {
93
+ if (isset($_POST['_breeze_nonce']) || wp_verify_nonce($_POST['_breeze_nonce'], 'breeze_settings')) {
94
+ //delete minify
95
+ Breeze_MinificationCache::clear_minification();
96
+ //clear normal cache
97
+ Breeze_PurgeCache::breeze_cache_flush();
98
+ //clear varnish cache
99
+ $this->purge_cache($homepage);
100
+ }
101
+ }
102
+ if(isset($_GET['breeze_purge']) && check_admin_referer('breeze_purge_cache')){
103
+ //clear varnish cache
104
+ $this->purge_cache($homepage);
105
+ //clear static cache
106
+ $size_cache = Breeze_Configuration::ajax_clean_cache($_GET['breeze_purge']);
107
+
108
+ if((int)$size_cache > 0){
109
+ echo '<div id="message-clear-cache-top" style="margin: 10px 0px 10px 0;padding: 10px;" class="notice notice-success" ><strong>'.__('OK ! Cache is cleaned: ','breeze') .$size_cache.__(' Kb static cache cleaned','breeze').'</strong></div>';
110
+ }else{
111
+ echo '<div id="message-clear-cache-top" style="margin: 10px 0px 10px 0;padding: 10px;" class="notice notice-success" ><strong>'.__('OK ! Cache file cleaned successfully','breeze').'</strong></div>';
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Purge varnish cache
119
+ *
120
+ */
121
+ public function purge_cache($url)
122
+ {
123
+ $parseUrl = parse_url($url);
124
+ $pregex = '';
125
+
126
+ // Default method is URLPURGE to purge only one object, this method is specific to cloudways configuration
127
+ $purge_method = 'URLPURGE';
128
+
129
+ // Use PURGE method when purging all site
130
+ if (isset($parseUrl['query']) && ($parseUrl['query'] == 'breeze')) {
131
+ // The regex is not needed as cloudways configuration purge all the cache of the domain when a PURGE is done
132
+ $pregex = '.*';
133
+ $purge_method = 'PURGE';
134
+ }
135
+
136
+ // Determine the path
137
+ $path = '';
138
+ if (isset($parseUrl['path'])) {
139
+ $path = $parseUrl['path'];
140
+ }
141
+
142
+ // Determine the schema
143
+ $schema = 'http://';
144
+ if (isset($parseUrl['scheme'])) {
145
+ $schema = $parseUrl['scheme'] . '://';
146
+ }
147
+
148
+ // Determine the host
149
+ $host = $parseUrl['host'];
150
+
151
+ $config = get_option('breeze_varnish_cache');
152
+ $varnish_host = isset($config['breeze-varnish-server-ip'])?$config['breeze-varnish-server-ip']:'127.0.0.1';
153
+
154
+ $purgeme = $varnish_host . $path . $pregex;
155
+
156
+ if (!empty($parseUrl['query']) && $parseUrl['query'] != 'breeze') {
157
+ $purgeme .= '?' . $parseUrl['query'];
158
+ }
159
+
160
+ $request_args = array('method' => $purge_method, 'headers' => array('Host' => $host, 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'), 'sslverify' => false);
161
+ $response = wp_remote_request($schema . $purgeme, $request_args);
162
+ if(is_wp_error($response) || $response['response']['code']!='200') {
163
+ if($schema==='https://') {
164
+ $schema = 'http://';
165
+ }else{
166
+ $schema = 'https://';
167
+ }
168
+ $response = wp_remote_request($schema . $purgeme, $request_args);
169
+ }
170
+ }
171
+
172
+ //check permission
173
+ public function check_permission()
174
+ {
175
+ return (!is_multisite() && current_user_can('activate_plugins')) || current_user_can('manage_network') || (is_multisite() && !current_user_can('manage_network') && (SUBDOMAIN_INSTALL || (!SUBDOMAIN_INSTALL && (BLOG_ID_CURRENT_SITE != $this->blogId))));
176
+ }
177
+ /*
178
+ * Purge varnish cache with action if id post exists
179
+ */
180
+ public function purge_post($postId)
181
+ {
182
+ $this->pushUrl($postId);
183
+ }
184
+ /*
185
+ * Purge varnish cache after comment
186
+ */
187
+ public function purge_post_on_comment($comment_ID, $approved, $commentdata){
188
+ if (empty($approved)) {
189
+ return;
190
+ }
191
+ $postId = $commentdata['comment_post_ID'];
192
+ $this->pushUrl($postId);
193
+ }
194
+ /*
195
+ * if a comments status changes, purge varnish
196
+ */
197
+ public function purge_post_on_comment_status($comment_ID, $comment_status){
198
+ $comment = get_comment($comment_ID);
199
+ if(!empty($comment)){
200
+ $postId = $comment->comment_post_ID;
201
+ $this->pushUrl($postId);
202
+ }
203
+ }
204
+ /*
205
+ * Push url from post ID
206
+ */
207
+ public function pushUrl($postId){
208
+ // If this is a valid post we want to purge the post,
209
+ // the home page and any associated tags and categories
210
+ $valid_post_status = array('publish', 'private', 'trash');
211
+ $this_post_status = get_post_status($postId);
212
+
213
+ // Not all post types are created equal
214
+ $invalid_post_type = array('nav_menu_item', 'revision');
215
+ $noarchive_post_type = array('post', 'page');
216
+ $this_post_type = get_post_type($postId);
217
+
218
+ // Determine the route for the rest API
219
+ // This will need to be revisted if WP updates the version.
220
+ $rest_api_route = 'wp/v2';
221
+
222
+ // array to collect all our URLs
223
+ $listofurls = array();
224
+
225
+ // Verify we have a permalink and that we're a valid post status and a not an invalid post type
226
+ if (get_permalink($postId) == true && in_array($this_post_status, $valid_post_status) && !in_array($this_post_type, $invalid_post_type)) {
227
+ // Post URL
228
+ array_push($listofurls, get_permalink($postId));
229
+
230
+ // JSON API Permalink for the post based on type
231
+ // We only want to do this if the rest_base exists
232
+ $post_type_object = get_post_type_object($this_post_type);
233
+ if (isset($post_type_object->rest_base) && !empty($post_type_object->rest_base)) {
234
+ $rest_permalink = get_rest_url() . $rest_api_route . '/' . $post_type_object->rest_base . '/' . $postId . '/';
235
+ } elseif ($this_post_type == 'post') {
236
+ $rest_permalink = get_rest_url() . $rest_api_route . '/posts/' . $postId . '/';
237
+ } elseif ($this_post_type == 'page') {
238
+ $rest_permalink = get_rest_url() . $rest_api_route . '/views/' . $postId . '/';
239
+ }
240
+ if(isset($rest_permalink))
241
+ array_push($listofurls, $rest_permalink);
242
+
243
+ // Add in AMP permalink if Automattic's AMP is installed
244
+ if (function_exists('amp_get_permalink')) {
245
+ array_push($listofurls, amp_get_permalink($postId));
246
+ }
247
+
248
+ // Regular AMP url for posts
249
+ array_push($listofurls, get_permalink($postId) . 'amp/');
250
+
251
+ // Also clean URL for trashed post.
252
+ if ($this_post_status == 'trash') {
253
+ $trashpost = get_permalink($postId);
254
+ $trashpost = str_replace('__trashed', '', $trashpost);
255
+ array_push($listofurls, $trashpost, $trashpost . 'feed/');
256
+ }
257
+
258
+ // Category purge based on Donnacha's work in WP Super Cache
259
+ $categories = get_the_category($postId);
260
+ if ($categories) {
261
+ foreach ($categories as $cat) {
262
+ array_push($listofurls,
263
+ get_category_link($cat->term_id),
264
+ get_rest_url() . $rest_api_route . '/categories/' . $cat->term_id . '/'
265
+ );
266
+ }
267
+ }
268
+ // Tag purge based on Donnacha's work in WP Super Cache
269
+ $tags = get_the_tags($postId);
270
+ if ($tags) {
271
+ foreach ($tags as $tag) {
272
+ array_push($listofurls,
273
+ get_tag_link($tag->term_id),
274
+ get_rest_url() . $rest_api_route . '/tags/' . $tag->term_id . '/'
275
+ );
276
+ }
277
+ }
278
+
279
+ // Author URL
280
+ $author_id = get_post_field('post_author', $postId);
281
+ array_push($listofurls,
282
+ get_author_posts_url($author_id),
283
+ get_author_feed_link($author_id),
284
+ get_rest_url() . $rest_api_route . '/users/' . $author_id . '/'
285
+ );
286
+
287
+
288
+ // Archives and their feeds
289
+ if ($this_post_type && !in_array($this_post_type, $noarchive_post_type)) {
290
+ array_push($listofurls,
291
+ get_post_type_archive_link(get_post_type($postId)),
292
+ get_post_type_archive_feed_link(get_post_type($postId))
293
+ // Need to add in JSON?
294
+ );
295
+ }
296
+
297
+
298
+ // Feeds
299
+ array_push($listofurls,
300
+ get_bloginfo_rss('rdf_url'),
301
+ get_bloginfo_rss('rss_url'),
302
+ get_bloginfo_rss('rss2_url'),
303
+ get_bloginfo_rss('atom_url'),
304
+ get_bloginfo_rss('comments_rss2_url'),
305
+ get_post_comments_feed_link($postId)
306
+ );
307
+
308
+ // Home Pages and (if used) posts page
309
+ array_push($listofurls,
310
+ get_rest_url(),
311
+ home_url() . '/'
312
+ );
313
+
314
+ if (get_option('show_on_front') == 'page') {
315
+ // Ensure we have a page_for_posts setting to avoid empty URL
316
+ if (get_option('page_for_posts')) {
317
+ array_push($listofurls, get_permalink(get_option('page_for_posts')));
318
+ }
319
+ }
320
+
321
+ } else {
322
+ // Nothing
323
+ return;
324
+ }
325
+
326
+ // Now flush all the URLs we've collected provided the array isn't empty
327
+ if (!empty($listofurls)) {
328
+ $purgeurls = array_unique($listofurls, SORT_REGULAR);
329
+
330
+ foreach ($purgeurls as $url) {
331
+ array_push($this->urlsPurge, $url);
332
+ }
333
+ }
334
+
335
+ }
336
+ }
337
+
338
+ new Breeze_PurgeVarnish();
inc/cdn-integration/breeze-cdn-integration.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_CDN_Integration{
24
+
25
+ public function __construct(){
26
+ add_action('template_redirect', array($this,'handle_rewrite_cdn'));
27
+ }
28
+
29
+ /**
30
+ * Execute rewrite cdn
31
+ */
32
+ public function handle_rewrite_cdn(){
33
+ $cdn_integration = get_option('breeze_cdn_integration');
34
+
35
+ if(empty($cdn_integration) || empty($cdn_integration['cdn-active'])){
36
+ return;
37
+ }
38
+
39
+ if($cdn_integration['cdn-url'] == ''){
40
+ return;
41
+ }
42
+
43
+ if(get_option('home') == $cdn_integration['cdn-url']){
44
+ return;
45
+ }
46
+
47
+ $rewrite = new Breeze_CDN_Rewrite($cdn_integration);
48
+
49
+ //rewrite CDN Url to html raw
50
+ // ob_start(array(&$rewrite,'rewrite'));
51
+ add_filter('breeze_cdn_content_return',array(&$rewrite,'rewrite'));
52
+
53
+ }
54
+
55
+ public static function instance(){
56
+ new self();
57
+ }
58
+ }
inc/cdn-integration/breeze-cdn-rewrite.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_CDN_Rewrite {
24
+ private $blog_url = null;
25
+ private $cdn_url = null;
26
+ private $dirs = array();
27
+ private $excludes = array();
28
+ private $relative = false;
29
+
30
+ public function __construct(&$option){
31
+ //storage option
32
+ $this->blog_url = get_option('home');
33
+ $this->cdn_url = $option['cdn-url'];
34
+ $this->dirs = $option['cdn-content'];
35
+ $this->excludes = $option['cdn-exclude-content'];
36
+ $this->relative = $option['cdn-relative-path'];
37
+ }
38
+ /*
39
+ * Replace cdn on html raw
40
+ */
41
+ public function rewrite($content){
42
+
43
+ $blog_url = quotemeta($this->blog_url);
44
+
45
+ // get dir scope in regex format
46
+ $dirs = $this->get_dir_scope();
47
+
48
+ // regex rule start
49
+ $regex_rule = '#(?<=[(\"\'])';
50
+
51
+ // check if relative paths
52
+ if ($this->relative) {
53
+ $regex_rule .= '(?:'.$blog_url.')?';
54
+ } else {
55
+ $regex_rule .= $blog_url;
56
+ }
57
+
58
+ // regex rule end
59
+ $regex_rule .= '/(?:((?:'.$dirs.')[^\"\')]+)|([^/\"\']+\.[^/\"\')]+))(?=[\"\')])#';
60
+
61
+ // call the cdn rewriter callback
62
+ $new_content = preg_replace_callback($regex_rule, array(&$this, 'replace_cdn_url'), $content);
63
+
64
+ return $new_content;
65
+ }
66
+
67
+ /**
68
+ * get directory scope
69
+ */
70
+
71
+ protected function get_dir_scope() {
72
+ // default
73
+ if (empty($this->dirs) || count($this->dirs) < 1) {
74
+ return 'wp\-content|wp\-includes';
75
+ }
76
+
77
+ return implode('|', array_map('quotemeta', array_map('trim', $this->dirs)));
78
+ }
79
+
80
+ /*
81
+ * Replace cdn url to root url
82
+ */
83
+ protected function replace_cdn_url($match){
84
+ //return file type or directories excluded
85
+ if($this->excludes_check($match[0])){
86
+ return $match[0];
87
+ }
88
+
89
+ $parseUrl = parse_url($this->blog_url);
90
+ $schema = 'http://';
91
+ if(isset($parseUrl['scheme'])){
92
+ $scheme = $parseUrl['scheme'].'://';
93
+ }
94
+ $host = $parseUrl['host'];
95
+ //get domain
96
+ $domain = $scheme.$host;
97
+
98
+ // check if not a relative path
99
+ if (!$this->relative || strstr($match[0], $this->blog_url)) {
100
+ return str_replace($domain, $this->cdn_url, $match[0]);
101
+ }
102
+
103
+ return $this->cdn_url . $match[0];
104
+
105
+ }
106
+ /*
107
+ * Check excludes assets
108
+ */
109
+ protected function excludes_check($dir){
110
+ if(!empty($this->excludes)){
111
+ foreach ($this->excludes as $exclude){
112
+ if(stristr($dir, $exclude) != false){
113
+ return true;
114
+ }
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ }
inc/minification/breeze-minification-base.php ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Based on some work of autoptimize plugin
4
+ */
5
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
6
+
7
+ abstract class Breeze_MinificationBase {
8
+ protected $content = '';
9
+ protected $tagWarning = false;
10
+
11
+ public function __construct($content) {
12
+ $this->content = $content;
13
+ }
14
+
15
+ //Reads the page and collects tags
16
+ abstract public function read($justhead);
17
+
18
+ //Joins and optimizes collected things
19
+ abstract public function minify();
20
+
21
+ //Caches the things
22
+ abstract public function cache();
23
+
24
+ //Returns the content
25
+ abstract public function getcontent();
26
+
27
+ //Converts an URL to a full path
28
+ protected function getpath($url) {
29
+ $url=apply_filters( 'breeze_filter_cssjs_alter_url', $url);
30
+
31
+ if (strpos($url,'%')!==false) {
32
+ $url=urldecode($url);
33
+ }
34
+
35
+ // normalize
36
+ if (strpos($url,'//')===0) {
37
+ if (is_ssl()) {
38
+ $url = "https:".$url;
39
+ } else {
40
+ $url = "http:".$url;
41
+ }
42
+ } else if ((strpos($url,'//')===false) && (strpos($url,parse_url(breeze_WP_SITE_URL,PHP_URL_HOST))===false)) {
43
+ $url = breeze_WP_SITE_URL.$url;
44
+ }
45
+
46
+ // first check; hostname wp site should be hostname of url
47
+ $thisHost=@parse_url($url,PHP_URL_HOST);
48
+ if ($thisHost!==parse_url(breeze_WP_SITE_URL,PHP_URL_HOST)) {
49
+ /*
50
+ * first try to get all domains from WPML (if available)
51
+ * then explicitely declare $this->cdn_url as OK as well
52
+ * then apply own filter autoptimize_filter_cssjs_multidomain takes an array of hostnames
53
+ * each item in that array will be considered part of the same WP multisite installation
54
+ */
55
+ $multidomains = array();
56
+
57
+ $multidomainsWPML = apply_filters('wpml_setting', array(), 'language_domains');
58
+ if (!empty($multidomainsWPML)) {
59
+ $multidomains = array_map(array($this,"ao_getDomain"),$multidomainsWPML);
60
+ }
61
+
62
+ if (!empty($this->cdn_url)) {
63
+ $multidomains[]=parse_url($this->cdn_url,PHP_URL_HOST);
64
+ }
65
+
66
+ $multidomains = apply_filters('breeze_filter_cssjs_multidomain', $multidomains);
67
+
68
+ if (!empty($multidomains)) {
69
+ if (in_array($thisHost,$multidomains)) {
70
+ $url=str_replace($thisHost, parse_url(breeze_WP_SITE_URL,PHP_URL_HOST), $url);
71
+ } else {
72
+ return false;
73
+ }
74
+ } else {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ // try to remove "wp root url" from url while not minding http<>https
80
+ $tmp_ao_root = preg_replace('/https?/','',breeze_WP_ROOT_URL);
81
+ $tmp_url = preg_replace('/https?/','',$url);
82
+ $path = str_replace($tmp_ao_root,'',$tmp_url);
83
+
84
+ // final check; if path starts with :// or //, this is not a URL in the WP context and we have to assume we can't aggregate
85
+ if (preg_match('#^:?//#',$path)) {
86
+ /** External script/css (adsense, etc) */
87
+ return false;
88
+ }
89
+
90
+ $path = str_replace('//','/',BREEZE_ROOT_DIR.$path);
91
+ return $path;
92
+ }
93
+
94
+ // needed for WPML-filter
95
+ protected function ao_getDomain($in) {
96
+ // make sure the url starts with something vaguely resembling a protocol
97
+ if ((strpos($in,"http")!==0) && (strpos($in,"//")!==0)) {
98
+ $in="http://".$in;
99
+ }
100
+
101
+ // do the actual parse_url
102
+ $out = parse_url($in,PHP_URL_HOST);
103
+
104
+ // fallback if parse_url does not understand the url is in fact a url
105
+ if (empty($out)) $out=$in;
106
+
107
+ return $out;
108
+ }
109
+
110
+
111
+ // logger
112
+ protected function ao_logger($logmsg,$appendHTML=true) {
113
+ if ($appendHTML) {
114
+ $logmsg="<!--noptimize--><!-- ".$logmsg." --><!--/noptimize-->";
115
+ $this->content.=$logmsg;
116
+ } else {
117
+ error_log("Error: ".$logmsg);
118
+ }
119
+ }
120
+
121
+ // hide everything between noptimize-comment tags
122
+ protected function hide_noptimize($noptimize_in) {
123
+ if ( preg_match( '/<!--\s?noptimize\s?-->/', $noptimize_in ) ) {
124
+ $noptimize_out = preg_replace_callback(
125
+ '#<!--\s?noptimize\s?-->.*?<!--\s?/\s?noptimize\s?-->#is',
126
+ create_function(
127
+ '$matches',
128
+ 'return "%%NOPTIMIZE".breeze_HASH."%%".base64_encode($matches[0])."%%NOPTIMIZE%%";'
129
+ ),
130
+ $noptimize_in
131
+ );
132
+ } else {
133
+ $noptimize_out = $noptimize_in;
134
+ }
135
+ return $noptimize_out;
136
+ }
137
+
138
+ // unhide noptimize-tags
139
+ protected function restore_noptimize($noptimize_in) {
140
+ if ( strpos( $noptimize_in, '%%NOPTIMIZE%%' ) !== false ) {
141
+ $noptimize_out = preg_replace_callback(
142
+ '#%%NOPTIMIZE'.breeze_HASH.'%%(.*?)%%NOPTIMIZE%%#is',
143
+ create_function(
144
+ '$matches',
145
+ 'return base64_decode($matches[1]);'
146
+ ),
147
+ $noptimize_in
148
+ );
149
+ } else {
150
+ $noptimize_out = $noptimize_in;
151
+ }
152
+ return $noptimize_out;
153
+ }
154
+
155
+ protected function hide_iehacks($iehacks_in) {
156
+ if ( strpos( $iehacks_in, '<!--[if' ) !== false ) {
157
+ $iehacks_out = preg_replace_callback(
158
+ '#<!--\[if.*?\[endif\]-->#is',
159
+ create_function(
160
+ '$matches',
161
+ 'return "%%IEHACK".breeze_HASH."%%".base64_encode($matches[0])."%%IEHACK%%";'
162
+ ),
163
+ $iehacks_in
164
+ );
165
+ } else {
166
+ $iehacks_out = $iehacks_in;
167
+ }
168
+ return $iehacks_out;
169
+ }
170
+
171
+ protected function restore_iehacks($iehacks_in) {
172
+ if ( strpos( $iehacks_in, '%%IEHACK%%' ) !== false ) {
173
+ $iehacks_out = preg_replace_callback(
174
+ '#%%IEHACK'.breeze_HASH.'%%(.*?)%%IEHACK%%#is',
175
+ create_function(
176
+ '$matches',
177
+ 'return base64_decode($matches[1]);'
178
+ ),
179
+ $iehacks_in
180
+ );
181
+ } else {
182
+ $iehacks_out=$iehacks_in;
183
+ }
184
+ return $iehacks_out;
185
+ }
186
+
187
+ protected function hide_comments($comments_in) {
188
+ if ( strpos( $comments_in, '<!--' ) !== false ) {
189
+ $comments_out = preg_replace_callback(
190
+ '#<!--.*?-->#is',
191
+ create_function(
192
+ '$matches',
193
+ 'return "%%COMMENTS".breeze_HASH."%%".base64_encode($matches[0])."%%COMMENTS%%";'
194
+ ),
195
+ $comments_in
196
+ );
197
+ } else {
198
+ $comments_out = $comments_in;
199
+ }
200
+ return $comments_out;
201
+ }
202
+
203
+ protected function restore_comments($comments_in) {
204
+ if ( strpos( $comments_in, '%%COMMENTS%%' ) !== false ) {
205
+ $comments_out = preg_replace_callback(
206
+ '#%%COMMENTS'.breeze_HASH.'%%(.*?)%%COMMENTS%%#is',
207
+ create_function(
208
+ '$matches',
209
+ 'return base64_decode($matches[1]);'
210
+ ),
211
+ $comments_in
212
+ );
213
+ } else {
214
+ $comments_out=$comments_in;
215
+ }
216
+ return $comments_out;
217
+ }
218
+
219
+ protected function url_replace_cdn( $url ) {
220
+ $cdn_url = apply_filters( 'breeze_filter_base_cdnurl', $this->cdn_url );
221
+ if ( !empty($cdn_url) ) {
222
+ // secondly prepend domain-less absolute URL's
223
+ if ( ( substr( $url, 0, 1 ) === '/' ) && ( substr( $url, 1, 1 ) !== '/' ) ) {
224
+ $url = rtrim( $cdn_url, '/' ) . $url;
225
+ } else {
226
+ // get wordpress base URL
227
+ $WPSiteBreakdown = parse_url( breeze_WP_SITE_URL );
228
+ $WPBaseUrl = $WPSiteBreakdown['scheme'] . '://' . $WPSiteBreakdown['host'];
229
+ if ( ! empty( $WPSiteBreakdown['port'] ) ) {
230
+ $WPBaseUrl .= ":" . $WPSiteBreakdown['port'];
231
+ }
232
+ // three: replace full url's with scheme
233
+ $tmp_url = str_replace( $WPBaseUrl, rtrim( $cdn_url, '/' ), $url );
234
+ if ( $tmp_url === $url ) {
235
+ // last attempt; replace scheme-less URL's
236
+ $url = str_replace( preg_replace( '/https?:/', '', $WPBaseUrl ), rtrim( $cdn_url, '/' ), $url );
237
+ } else {
238
+ $url = $tmp_url;
239
+ }
240
+ }
241
+ }
242
+
243
+ // allow API filter to take alter after CDN replacement
244
+ $url = apply_filters( 'breeze_filter_base_replace_cdn', $url );
245
+ return $url;
246
+ }
247
+
248
+ protected function inject_in_html($payload,$replaceTag) {
249
+ if (strpos($this->content,$replaceTag[0])!== false) {
250
+ if ($replaceTag[1]==="after") {
251
+ $replaceBlock=$replaceTag[0].$payload;
252
+ } else if ($replaceTag[1]==="replace"){
253
+ $replaceBlock=$payload;
254
+ } else {
255
+ $replaceBlock=$payload.$replaceTag[0];
256
+ }
257
+ $this->content = substr_replace($this->content,$replaceBlock,strpos($this->content,$replaceTag[0]),strlen($replaceTag[0]));
258
+ } else {
259
+ $this->content .= $payload;
260
+ if (!$this->tagWarning) {
261
+ $this->content .= "<!--noptimize--><!-- breeze found a problem with the HTML in your Theme, tag ".$replaceTag[0]." missing --><!--/noptimize-->";
262
+ $this->tagWarning=true;
263
+ }
264
+ }
265
+ }
266
+
267
+ protected function isremovable($tag, $removables) {
268
+ foreach ($removables as $match) {
269
+ if (strpos($tag,$match)!==false) {
270
+ return true;
271
+ }
272
+ }
273
+ return false;
274
+ }
275
+
276
+ // inject already minified code in optimized JS/CSS
277
+ protected function inject_minified($in) {
278
+ if ( strpos( $in, '%%INJECTLATER%%' ) !== false ) {
279
+ $out = preg_replace_callback(
280
+ '#%%INJECTLATER'.breeze_HASH.'%%(.*?)%%INJECTLATER%%#is',
281
+ create_function(
282
+ '$matches',
283
+ '$filepath=base64_decode(strtok($matches[1],"|"));
284
+ $filecontent=file_get_contents($filepath);
285
+
286
+ // remove BOM
287
+ $filecontent = preg_replace("#\x{EF}\x{BB}\x{BF}#","",$filecontent);
288
+
289
+ // remove comments and blank lines
290
+ if (substr($filepath,-3,3)===".js") {
291
+ $filecontent=preg_replace("#^\s*\/\/.*$#Um","",$filecontent);
292
+ }
293
+
294
+ $filecontent=preg_replace("#^\s*\/\*[^!].*\*\/\s?#Us","",$filecontent);
295
+ $filecontent=preg_replace("#(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+#", "\n", $filecontent);
296
+
297
+ // specific stuff for JS-files
298
+ if (substr($filepath,-3,3)===".js") {
299
+ if ((substr($filecontent,-1,1)!==";")&&(substr($filecontent,-1,1)!=="}")) {
300
+ $filecontent.=";";
301
+ }
302
+
303
+ if (get_option("breeze_js_trycatch")==="on") {
304
+ $filecontent="try{".$filecontent."}catch(e){}";
305
+ }
306
+ } else if ((substr($filepath,-4,4)===".css")) {
307
+ $filecontent=Breeze_MinificationStyles::fixurls($filepath,$filecontent);
308
+ }
309
+
310
+ // return
311
+ return "\n".$filecontent;'
312
+ ),
313
+ $in
314
+ );
315
+ } else {
316
+ $out = $in;
317
+ }
318
+ return $out;
319
+ }
320
+ }
inc/minification/breeze-minification-cache.php ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+ /*
4
+ * Based on some work of autoptimize plugin
5
+ */
6
+ class Breeze_MinificationCache {
7
+ private $filename;
8
+ private $mime;
9
+ private $cachedir;
10
+ private $delayed;
11
+
12
+ public function __construct($md5,$ext='php') {
13
+ $this->cachedir = BREEZE_MINIFICATION_CACHE;
14
+ if(is_multisite()){
15
+ $blog_id = get_current_blog_id();
16
+ $this->cachedir = BREEZE_MINIFICATION_CACHE.$blog_id . '/';
17
+ }
18
+ $this->delayed = BREEZE_CACHE_DELAY;
19
+ $this->nogzip = BREEZE_CACHE_NOGZIP;
20
+ if($this->nogzip == false) {
21
+ $this->filename = BREEZE_CACHEFILE_PREFIX.$md5.'.php';
22
+ } else {
23
+ if (in_array($ext, array("js","css"))) {
24
+ $this->filename = $ext.'/'.BREEZE_CACHEFILE_PREFIX.$md5.'.'.$ext;
25
+ } else {
26
+ $this->filename = '/'.BREEZE_CACHEFILE_PREFIX.$md5.'.'.$ext;
27
+ }
28
+ }
29
+
30
+
31
+ }
32
+
33
+ public function check() {
34
+ if(!file_exists($this->cachedir.$this->filename)) {
35
+ // No cached file, sorry
36
+ return false;
37
+ }
38
+ // Cache exists!
39
+ return true;
40
+ }
41
+ public function retrieve() {
42
+ if($this->check()) {
43
+ if($this->nogzip == false) {
44
+ return file_get_contents($this->cachedir.$this->filename.'.none');
45
+ } else {
46
+ return file_get_contents($this->cachedir.$this->filename);
47
+ }
48
+ }
49
+ return false;
50
+ }
51
+ public function cache($code,$mime) {
52
+ if($this->nogzip == false) {
53
+ $file = ($this->delayed ? 'delayed.php' : 'default.php');
54
+ $phpcode = file_get_contents(BREEZE_PLUGIN_DIR.'/inc/minification/config/'.$file);
55
+ $phpcode = str_replace(array('%%CONTENT%%','exit;'),array($mime,''),$phpcode);
56
+ file_put_contents($this->cachedir.$this->filename,$phpcode, LOCK_EX);
57
+ file_put_contents($this->cachedir.$this->filename.'.none',$code, LOCK_EX);
58
+ if(!$this->delayed) {
59
+ // Compress now!
60
+ file_put_contents($this->cachedir.$this->filename.'.deflate',gzencode($code,9,FORCE_DEFLATE), LOCK_EX);
61
+ file_put_contents($this->cachedir.$this->filename.'.gzip',gzencode($code,9,FORCE_GZIP), LOCK_EX);
62
+ }
63
+ } else {
64
+ // Write code to cache without doing anything else
65
+ file_put_contents($this->cachedir.$this->filename,$code, LOCK_EX);
66
+ }
67
+ }
68
+ public function getname() {
69
+ apply_filters('breeze_filter_cache_getname',breeze_CACHE_URL.$this->filename);
70
+ return $this->filename;
71
+ }
72
+ //create folder cache
73
+ public static function create_cache_minification_folder(){
74
+ if(!defined('BREEZE_MINIFICATION_CACHE')) {
75
+ // We didn't set a cache
76
+ return false;
77
+ }
78
+ if(is_multisite()){
79
+ $blog_id = get_current_blog_id();
80
+ foreach (array("","js","css") as $checkDir) {
81
+ if(!Breeze_MinificationCache::checkCacheDir(BREEZE_MINIFICATION_CACHE. $blog_id .'/'.$checkDir)) {
82
+ return false;
83
+ }
84
+ }
85
+
86
+ /** write index.html here to avoid prying eyes */
87
+ $indexFile=BREEZE_MINIFICATION_CACHE . $blog_id .'/index.html';
88
+ if(!is_file($indexFile)) {
89
+ @file_put_contents($indexFile,'<html><head><meta name="robots" content="noindex, nofollow"></head><body></body></html>');
90
+ }
91
+
92
+ /** write .htaccess here to overrule wp_super_cache */
93
+ $htAccess=BREEZE_MINIFICATION_CACHE. $blog_id .'/.htaccess';
94
+ }else{
95
+ foreach (array("","js","css") as $checkDir) {
96
+ if(!Breeze_MinificationCache::checkCacheDir(BREEZE_MINIFICATION_CACHE.$checkDir)) {
97
+ return false;
98
+ }
99
+ }
100
+ /** write index.html here to avoid prying eyes */
101
+ $indexFile=BREEZE_MINIFICATION_CACHE.'/index.html';
102
+ if(!is_file($indexFile)) {
103
+ @file_put_contents($indexFile,'<html><head><meta name="robots" content="noindex, nofollow"></head><body></body></html>');
104
+ }
105
+
106
+ /** write .htaccess here to overrule wp_super_cache */
107
+ $htAccess=BREEZE_MINIFICATION_CACHE.'/.htaccess';
108
+ }
109
+
110
+ if(!is_file($htAccess)) {
111
+ /**
112
+ * create wp-content/AO_htaccess_tmpl with
113
+ * whatever htaccess rules you might need
114
+ * if you want to override default AO htaccess
115
+ */
116
+ $htaccess_tmpl=WP_CONTENT_DIR."/AO_htaccess_tmpl";
117
+ if (is_file($htaccess_tmpl)) {
118
+ $htAccessContent=file_get_contents($htaccess_tmpl);
119
+ } else if (is_multisite()) {
120
+ $htAccessContent='<IfModule mod_headers.c>
121
+ Header set Vary "Accept-Encoding"
122
+ Header set Cache-Control "max-age=10672000, must-revalidate"
123
+ </IfModule>
124
+ <IfModule mod_expires.c>
125
+ ExpiresActive On
126
+ ExpiresByType text/css A30672000
127
+ ExpiresByType text/javascript A30672000
128
+ ExpiresByType application/javascript A30672000
129
+ </IfModule>
130
+ <IfModule mod_deflate.c>
131
+ <FilesMatch "\.(js|css)$">
132
+ SetOutputFilter DEFLATE
133
+ </FilesMatch>
134
+ </IfModule>
135
+ <IfModule mod_authz_core.c>
136
+ <Files *.php>
137
+ Require all granted
138
+ </Files>
139
+ </IfModule>
140
+ <IfModule !mod_authz_core.c>
141
+ <Files *.php>
142
+ Order allow,deny
143
+ Allow from all
144
+ </Files>
145
+ </IfModule>';
146
+ } else {
147
+ $htAccessContent='<IfModule mod_headers.c>
148
+ Header set Vary "Accept-Encoding"
149
+ Header set Cache-Control "max-age=10672000, must-revalidate"
150
+ </IfModule>
151
+ <IfModule mod_expires.c>
152
+ ExpiresActive On
153
+ ExpiresByType text/css A30672000
154
+ ExpiresByType text/javascript A30672000
155
+ ExpiresByType application/javascript A30672000
156
+ </IfModule>
157
+ <IfModule mod_deflate.c>
158
+ <FilesMatch "\.(js|css)$">
159
+ SetOutputFilter DEFLATE
160
+ </FilesMatch>
161
+ </IfModule>
162
+ <IfModule mod_authz_core.c>
163
+ <Files *.php>
164
+ Require all denied
165
+ </Files>
166
+ </IfModule>
167
+ <IfModule !mod_authz_core.c>
168
+ <Files *.php>
169
+ Order deny,allow
170
+ Deny from all
171
+ </Files>
172
+ </IfModule>';
173
+ }
174
+
175
+ @file_put_contents($htAccess,$htAccessContent);
176
+ }
177
+ // All OK
178
+ return true;
179
+
180
+ }
181
+ // check dir cache
182
+ static function checkCacheDir($dir) {
183
+ // Check and create if not exists
184
+ if(!file_exists($dir)) {
185
+ @mkdir($dir,0775,true);
186
+ if(!file_exists($dir)) {
187
+ return false;
188
+ }
189
+ }
190
+
191
+ // check if we can now write
192
+ if(!is_writable($dir)) {
193
+ return false;
194
+ }
195
+
196
+ // and write index.html here to avoid prying eyes
197
+ $indexFile=$dir.'/index.html';
198
+ if(!is_file($indexFile)) {
199
+ @file_put_contents($indexFile,'<html><head><meta name="robots" content="noindex, nofollow"></head><body></body></html>');
200
+ }
201
+
202
+ return true;
203
+ }
204
+ public static function clear_minification() {
205
+ if(!Breeze_MinificationCache::create_cache_minification_folder()) {
206
+ return false;
207
+ }
208
+ if(is_multisite()){
209
+ $blog_id = get_current_blog_id();
210
+ // scan the cachedirs
211
+ foreach (array("","js","css") as $scandirName) {
212
+ $scan[$scandirName] = scandir(BREEZE_MINIFICATION_CACHE.$blog_id.'/'.$scandirName);
213
+ }
214
+ // clear the cachedirs
215
+ foreach ($scan as $scandirName=>$scanneddir) {
216
+ $thisAoCacheDir=rtrim(BREEZE_MINIFICATION_CACHE.$blog_id.'/'.$scandirName,"/")."/";
217
+ foreach($scanneddir as $file) {
218
+ if(!in_array($file,array('.','..')) && strpos($file,BREEZE_CACHEFILE_PREFIX) !== false && is_file($thisAoCacheDir.$file)) {
219
+ @unlink($thisAoCacheDir.$file);
220
+ }
221
+ }
222
+ }
223
+ @unlink(BREEZE_MINIFICATION_CACHE.$blog_id."/.htaccess");
224
+ }else{
225
+ // scan the cachedirs
226
+ foreach (array("","js","css") as $scandirName) {
227
+ $scan[$scandirName] = scandir(BREEZE_MINIFICATION_CACHE.$scandirName);
228
+ }
229
+ // clear the cachedirs
230
+ foreach ($scan as $scandirName=>$scanneddir) {
231
+ $thisAoCacheDir=rtrim(BREEZE_MINIFICATION_CACHE.$scandirName,"/")."/";
232
+ foreach($scanneddir as $file) {
233
+ if(!in_array($file,array('.','..')) && strpos($file,BREEZE_CACHEFILE_PREFIX) !== false && is_file($thisAoCacheDir.$file)) {
234
+ @unlink($thisAoCacheDir.$file);
235
+ }
236
+ }
237
+ }
238
+
239
+ @unlink(BREEZE_MINIFICATION_CACHE."/.htaccess");
240
+ }
241
+ return true;
242
+ }
243
+
244
+ public static function factory() {
245
+
246
+ static $instance;
247
+
248
+ if ( ! $instance ) {
249
+ $instance = new self();
250
+ $instance->set_action();
251
+ }
252
+
253
+ return $instance;
254
+ }
255
+ }
256
+
inc/minification/breeze-minification-html.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Based on some work of autoptimize plugin
4
+ */
5
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
6
+
7
+ class Breeze_MinificationHtml extends Breeze_MinificationBase {
8
+ private $keepcomments = false;
9
+ private $exclude = array('<!-- ngg_resource_manager_marker -->');
10
+
11
+ public function read($options) {
12
+ // Remove the HTML comments?
13
+ $this->keepcomments = (bool) $options['keepcomments'];
14
+
15
+ // filter to force xhtml
16
+ $this->forcexhtml = (bool) apply_filters( 'breeze_filter_html_forcexhtml', false );
17
+
18
+ // filter to add strings to be excluded from HTML minification
19
+ $excludeHTML = apply_filters( 'breeze_filter_html_exclude','' );
20
+ if ($excludeHTML!=="") {
21
+ $exclHTMLArr = array_filter(array_map('trim',explode(",",$excludeHTML)));
22
+ $this->exclude = array_merge($exclHTMLArr,$this->exclude);
23
+ }
24
+
25
+ // Nothing else for HTML
26
+ return true;
27
+ }
28
+
29
+ //Joins and optimizes CSS
30
+ public function minify() {
31
+ $noptimizeHTML = apply_filters( 'breeze_filter_html_noptimize', false, $this->content );
32
+ if ($noptimizeHTML)
33
+ return false;
34
+
35
+ if(class_exists('Minify_HTML')) {
36
+ // wrap the to-be-excluded strings in noptimize tags
37
+ foreach ($this->exclude as $exclString) {
38
+ if (strpos($this->content,$exclString)!==false) {
39
+ $replString="<!--noptimize-->".$exclString."<!--/noptimize-->";
40
+ $this->content=str_replace($exclString,$replString,$this->content);
41
+ }
42
+ }
43
+
44
+ // noptimize me
45
+ $this->content = $this->hide_noptimize($this->content);
46
+
47
+ // Minify html
48
+ $options = array('keepComments' => $this->keepcomments);
49
+ if ($this->forcexhtml) {
50
+ $options['xhtml'] = true;
51
+ }
52
+
53
+ if (@is_callable(array(new Minify_HTML,"minify"))) {
54
+ $tmp_content = Minify_HTML::minify($this->content,$options);
55
+ if (!empty($tmp_content)) {
56
+ $this->content = $tmp_content;
57
+ unset($tmp_content);
58
+ }
59
+ }
60
+
61
+ // restore noptimize
62
+ $this->content = $this->restore_noptimize($this->content);
63
+
64
+ // remove the noptimize-wrapper from around the excluded strings
65
+ foreach ($this->exclude as $exclString) {
66
+ $replString="<!--noptimize-->".$exclString."<!--/noptimize-->";
67
+ if (strpos($this->content,$replString)!==false) {
68
+ $this->content=str_replace($replString,$exclString,$this->content);
69
+ }
70
+ }
71
+
72
+ return true;
73
+ }
74
+
75
+ // Didn't minify :(
76
+ return false;
77
+ }
78
+
79
+ // Does nothing
80
+ public function cache() {
81
+ //No cache for HTML
82
+ return true;
83
+ }
84
+
85
+ //Returns the content
86
+ public function getcontent() {
87
+ return $this->content;
88
+ }
89
+ }
inc/minification/breeze-minification-scripts.php ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Based on some work of autoptimize plugin
4
+ */
5
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
6
+
7
+ class Breeze_MinificationScripts extends Breeze_MinificationBase {
8
+ private $scripts = array();
9
+ private $dontmove = array('document.write','html5.js','show_ads.js','google_ad','blogcatalog.com/w','tweetmeme.com/i','mybloglog.com/','histats.com/js','ads.smowtion.com/ad.js','statcounter.com/counter/counter.js','widgets.amung.us','ws.amazon.com/widgets','media.fastclick.net','/ads/','comment-form-quicktags/quicktags.php','edToolbar','intensedebate.com','scripts.chitika.net/','_gaq.push','jotform.com/','admin-bar.min.js','GoogleAnalyticsObject','plupload.full.min.js','syntaxhighlighter','adsbygoogle','gist.github.com','_stq','nonce','post_id','data-noptimize');
10
+ private $domove = array('gaJsHost','load_cmc','jd.gallery.transitions.js','swfobject.embedSWF(','tiny_mce.js','tinyMCEPreInit.go');
11
+ private $domovelast = array('addthis.com','/afsonline/show_afs_search.js','disqus.js','networkedblogs.com/getnetworkwidget','infolinks.com/js/','jd.gallery.js.php','jd.gallery.transitions.js','swfobject.embedSWF(','linkwithin.com/widget.js','tiny_mce.js','tinyMCEPreInit.go');
12
+ private $trycatch = false;
13
+ private $alreadyminified = false;
14
+ private $forcehead = true;
15
+ private $include_inline = false;
16
+ private $jscode = '';
17
+ private $url = '';
18
+ private $move = array('first' => array(), 'last' => array());
19
+ private $restofcontent = '';
20
+ private $md5hash = '';
21
+ private $whitelist = '';
22
+ private $jsremovables = array();
23
+ private $inject_min_late = '';
24
+ private $group_js = false;
25
+ private $custom_js_exclude = array();
26
+ private $js_group_val = array();
27
+ private $js_min_arr = array();
28
+ private $url_group_arr = array();
29
+ //Reads the page and collects script tags
30
+ public function read($options) {
31
+ $noptimizeJS = apply_filters( 'breeze_filter_js_noptimize', false, $this->content );
32
+ if ($noptimizeJS) return false;
33
+
34
+ // only optimize known good JS?
35
+ $whitelistJS = apply_filters( 'breeze_filter_js_whitelist', "" );
36
+ if (!empty($whitelistJS)) {
37
+ $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistJS)));
38
+ }
39
+
40
+ // is there JS we should simply remove
41
+ $removableJS = apply_filters( 'breeze_filter_js_removables', '');
42
+ if (!empty($removableJS)) {
43
+ $this->jsremovables = array_filter(array_map('trim',explode(",",$removableJS)));
44
+ }
45
+
46
+ // only header?
47
+ if( apply_filters('breeze_filter_js_justhead',$options['justhead']) == true ) {
48
+ $content = explode('</head>',$this->content,2);
49
+ $this->content = $content[0].'</head>';
50
+ $this->restofcontent = $content[1];
51
+ }
52
+
53
+ // include inline?
54
+ if( apply_filters('breeze_js_include_inline',$options['include_inline']) == true ) {
55
+ $this->include_inline = true;
56
+ }
57
+
58
+ // group js?
59
+ if( apply_filters('breeze_js_group_js',$options['group_js']) == true ) {
60
+ $this->group_js = true;
61
+ }
62
+
63
+ //custom js exclude
64
+ if(!empty($options['custom_js_exclude'])){
65
+ $this->custom_js_exclude = $options['custom_js_exclude'];
66
+ }
67
+
68
+ // filter to "late inject minified JS", default to true for now (it is faster)
69
+ $this->inject_min_late = apply_filters('breeze_filter_js_inject_min_late',true);
70
+
71
+ // filters to override hardcoded do(nt)move(last) array contents (array in, array out!)
72
+ $this->dontmove = apply_filters( 'breeze_js_dontmove', $this->dontmove );
73
+ $this->domovelast = apply_filters( 'breeze_filter_js_movelast', $this->domovelast );
74
+ $this->domove = apply_filters( 'breeze_filter_js_domove', $this->domove );
75
+
76
+ // get extra exclusions settings or filter
77
+ $excludeJS = $options['js_exclude'];
78
+ $excludeJS = apply_filters( 'breeze_filter_js_exclude', $excludeJS );
79
+ if ($excludeJS!=="") {
80
+ $exclJSArr = array_filter(array_map('trim',explode(",",$excludeJS)));
81
+ $this->dontmove = array_merge($exclJSArr,$this->dontmove);
82
+ }
83
+
84
+ //Should we add try-catch?
85
+ if($options['trycatch'] == true)
86
+ $this->trycatch = true;
87
+
88
+ // force js in head?
89
+ if($options['forcehead'] == true) {
90
+ $this->forcehead = true;
91
+ } else {
92
+ $this->forcehead = false;
93
+ }
94
+ $this->forcehead = apply_filters( 'breeze_filter_js_forcehead', $this->forcehead );
95
+
96
+ // get cdn url
97
+ $this->cdn_url = $options['cdn_url'];
98
+
99
+ // noptimize me
100
+ $this->content = $this->hide_noptimize($this->content);
101
+
102
+ // Save IE hacks
103
+ $this->content = $this->hide_iehacks($this->content);
104
+
105
+ // comments
106
+ $this->content = $this->hide_comments($this->content);
107
+
108
+ //Get script files
109
+ if(preg_match_all('#<script.*</script>#Usmi',$this->content,$matches)) {
110
+ foreach($matches[0] as $tag) {
111
+ // only consider aggregation whitelisted in should_aggregate-function
112
+ if( !$this->should_aggregate($tag) ) {
113
+ $tag='';
114
+ continue;
115
+ }
116
+ if(preg_match('#src=("|\')(.*)("|\')#Usmi',$tag,$source)) {
117
+ if ($this->isremovable($tag,$this->jsremovables)) {
118
+ $this->content = str_replace($tag,'',$this->content);
119
+ continue;
120
+ }
121
+ // External script
122
+ $url = current(explode('?',$source[2],2));
123
+ //exclude js
124
+ if(in_array($url,$this->custom_js_exclude)){
125
+ continue;
126
+ }
127
+
128
+ $path = $this->getpath($url);
129
+ if($path !== false && preg_match('#\.js$#',$path)) {
130
+ //Inline
131
+ if($this->ismergeable($tag)) {
132
+ //We can merge it
133
+ $this->scripts[] = $path;
134
+
135
+ } else {
136
+ //No merge, but maybe we can move it
137
+ if($this->ismovable($tag)) {
138
+ //Yeah, move it
139
+ if($this->movetolast($tag)) {
140
+ $this->move['last'][] = $tag;
141
+ } else {
142
+ $this->move['first'][] = $tag;
143
+ }
144
+ } else {
145
+ //We shouldn't touch this
146
+ $tag = '';
147
+ }
148
+ }
149
+ } else {
150
+ //External script (example: google analytics)
151
+ //OR Script is dynamic (.php etc)
152
+ if($this->ismovable($tag)) {
153
+ if($this->movetolast($tag)) {
154
+ $this->move['last'][] = $tag;
155
+ } else {
156
+ $this->move['first'][] = $tag;
157
+ }
158
+ } else {
159
+ //We shouldn't touch this
160
+ $tag = '';
161
+ }
162
+ }
163
+ } else {
164
+ // Inline script
165
+ if ($this->isremovable($tag,$this->jsremovables)) {
166
+ $this->content = str_replace($tag,'',$this->content);
167
+ continue;
168
+ }
169
+
170
+ // unhide comments, as javascript may be wrapped in comment-tags for old times' sake
171
+ $tag = $this->restore_comments($tag);
172
+ if($this->ismergeable($tag) && ( $this->include_inline )) {
173
+ preg_match('#<script.*>(.*)</script>#Usmi',$tag,$code);
174
+ $code = preg_replace('#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm','$1',$code[1]);
175
+ $code = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/','',$code);
176
+ $this->scripts[] = 'INLINE;'.$code;
177
+ } else {
178
+ // Can we move this?
179
+ if($this->ismovable($tag)) {
180
+ if($this->movetolast($tag)) {
181
+ $this->move['last'][] = $tag;
182
+ } else {
183
+ $this->move['first'][] = $tag;
184
+ }
185
+ } else {
186
+ //We shouldn't touch this
187
+ $tag = '';
188
+ }
189
+ }
190
+ // re-hide comments to be able to do the removal based on tag from $this->content
191
+ $tag = $this->hide_comments($tag);
192
+ }
193
+ //Remove the original script tag
194
+ $this->content = str_replace($tag,'',$this->content);
195
+ }
196
+
197
+ return true;
198
+ }
199
+
200
+ // No script files, great ;-)
201
+ return false;
202
+ }
203
+
204
+ //Joins and optimizes JS
205
+ public function minify() {
206
+ foreach($this->scripts as $script) {
207
+ if(preg_match('#^INLINE;#',$script)) {
208
+ //Inline script
209
+ $script = preg_replace('#^INLINE;#','',$script);
210
+ $script = rtrim( $script, ";\n\t\r" ) . ';';
211
+ //Add try-catch?
212
+ if($this->trycatch) {
213
+ $script = 'try{'.$script.'}catch(e){}';
214
+ }
215
+ $tmpscript = apply_filters( 'breeze_js_individual_script', $script, "" );
216
+ if ( has_filter('breeze_js_individual_script') && !empty($tmpscript) ) {
217
+ $script=$tmpscript;
218
+ $this->alreadyminified=true;
219
+ }
220
+ if($this->group_js == true){
221
+ $this->jscode .= "\n" . $script;
222
+ }else{
223
+ $this->js_group_val[] = $script;
224
+ }
225
+
226
+ } else {
227
+ //External script
228
+ if($script !== false && file_exists($script) && is_readable($script)) {
229
+ $scriptsrc = file_get_contents($script);
230
+ $scriptsrc = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$scriptsrc);
231
+ $scriptsrc = rtrim($scriptsrc,";\n\t\r").';';
232
+ //Add try-catch?
233
+ if($this->trycatch) {
234
+ $scriptsrc = 'try{'.$scriptsrc.'}catch(e){}';
235
+ }
236
+ $tmpscriptsrc = apply_filters( 'breeze_js_individual_script', $scriptsrc, $script );
237
+ if ( has_filter('breeze_js_individual_script') && !empty($tmpscriptsrc) ) {
238
+ $scriptsrc=$tmpscriptsrc;
239
+ $this->alreadyminified=true;
240
+ } else if ((strpos($script,"min.js")!==false) && ($this->inject_min_late===true)) {
241
+ $scriptsrc="%%INJECTLATER".breeze_HASH."%%".base64_encode($script)."|".md5($scriptsrc)."%%INJECTLATER%%";
242
+ }
243
+ if($this->group_js == true){
244
+ $this->jscode .= "\n" . $scriptsrc;
245
+ }else{
246
+ $this->js_group_val[] = $scriptsrc;
247
+ }
248
+
249
+ }/*else{
250
+ //Couldn't read JS. Maybe getpath isn't working?
251
+ }*/
252
+ }
253
+ }
254
+ if($this->group_js == true){
255
+ //Check for already-minified code
256
+ $this->md5hash = md5($this->jscode);
257
+ $ccheck = new Breeze_MinificationCache($this->md5hash,'js');
258
+ if($ccheck->check()) {
259
+ $this->jscode = $ccheck->retrieve();
260
+ return true;
261
+ }
262
+ unset($ccheck);
263
+
264
+ //$this->jscode has all the uncompressed code now.
265
+ if ($this->alreadyminified!==true) {
266
+ if (class_exists('JSMin') && apply_filters( 'breeze_js_do_minify' , true)) {
267
+ if (@is_callable(array("JSMin","minify"))) {
268
+ $tmp_jscode = trim(JSMin::minify($this->jscode));
269
+ if (!empty($tmp_jscode)) {
270
+ $this->jscode = $tmp_jscode;
271
+ unset($tmp_jscode);
272
+ }
273
+ $this->jscode = $this->inject_minified($this->jscode);
274
+ $this->jscode = apply_filters( 'breeze_js_after_minify', $this->jscode );
275
+ return true;
276
+ } else {
277
+ $this->jscode = $this->inject_minified($this->jscode);
278
+ return false;
279
+ }
280
+ } else {
281
+ $this->jscode = $this->inject_minified($this->jscode);
282
+ return false;
283
+ }
284
+ }
285
+ }else{
286
+ foreach ($this->js_group_val as $jscode){
287
+ //Check for already-minified code
288
+ $this->md5hash = md5($jscode);
289
+ $ccheck = new Breeze_MinificationCache($this->md5hash,'js');
290
+ if($ccheck->check()) {
291
+ $js_exist = $ccheck->retrieve();
292
+ $this->js_min_arr[] = $this->md5hash .'_breezejsgroup_'.$js_exist;
293
+ continue;
294
+ }
295
+ unset($ccheck);
296
+
297
+ //$this->jscode has all the uncompressed code now.
298
+
299
+ if (class_exists('JSMin') && apply_filters( 'breeze_js_do_minify' , true)) {
300
+ if (@is_callable(array("JSMin","minify"))) {
301
+ $tmp_jscode = trim(JSMin::minify($jscode));
302
+ if (!empty($tmp_jscode)) {
303
+ $jscode = $tmp_jscode;
304
+ unset($tmp_jscode);
305
+ }
306
+ $jscode = $this->inject_minified($jscode);
307
+ $jscode = apply_filters( 'breeze_js_after_minify', $jscode );
308
+ $this->js_min_arr[] = $this->md5hash .'_breezejsgroup_'.$jscode;
309
+ } else {
310
+ $jscode = $this->inject_minified($jscode);
311
+ $this->js_min_arr[] = $this->md5hash .'_breezejsgroup_'.$jscode;
312
+
313
+ }
314
+ } else {
315
+ $jscode = $this->inject_minified($jscode);
316
+ $this->js_min_arr[] = $this->md5hash .'_breezejsgroup_'.$jscode;
317
+ }
318
+
319
+ }
320
+ }
321
+ return true;
322
+ }
323
+
324
+ //Caches the JS in uncompressed, deflated and gzipped form.
325
+ public function cache() {
326
+ if($this->group_js == true){
327
+ $cache = new Breeze_MinificationCache($this->md5hash,'js');
328
+ if(!$cache->check()) {
329
+ //Cache our code
330
+ $cache->cache($this->jscode,'text/javascript');
331
+ }
332
+ $this->url = breeze_CACHE_URL.$cache->getname();
333
+ $this->url = $this->url_replace_cdn($this->url);
334
+ }else{
335
+ foreach ($this->js_min_arr as $js_min){
336
+ $namehash = substr($js_min, 0 , strpos($js_min,'_breezejsgroup_'));
337
+ $js_code = substr($js_min,strpos($js_min,'_breezejsgroup_')+strlen('_breezejsgroup_'));
338
+ $cache = new Breeze_MinificationCache($namehash,'js');
339
+ if(!$cache->check()) {
340
+ //Cache our code
341
+ $cache->cache($js_code,'text/javascript');
342
+ }
343
+ $url = breeze_CACHE_URL.$cache->getname();
344
+ $this->url_group_arr[]= $this->url_replace_cdn($url);
345
+ }
346
+ }
347
+ }
348
+
349
+ // Returns the content
350
+ public function getcontent() {
351
+ // Restore the full content
352
+ if(!empty($this->restofcontent)) {
353
+ $this->content .= $this->restofcontent;
354
+ $this->restofcontent = '';
355
+ }
356
+
357
+ // Add the scripts taking forcehead/ deferred (default) into account
358
+ if($this->forcehead == true) {
359
+ $replaceTag=array("</head>","before");
360
+ $defer="";
361
+ } else {
362
+ $replaceTag=array("</body>","before");
363
+ $defer="defer ";
364
+ }
365
+
366
+ $defer = apply_filters( 'breeze_filter_js_defer', $defer );
367
+ if($this->group_js == true){
368
+ $bodyreplacementpayload = '<script type="text/javascript" '.$defer.'src="'.$this->url.'"></script>';
369
+ $bodyreplacementpayload = apply_filters('breeze_filter_js_bodyreplacementpayload',$bodyreplacementpayload);
370
+ }else{
371
+ $arrscript = array();
372
+ foreach ($this->url_group_arr as $url){
373
+ $arrscript[] = '<script type="text/javascript" '.$defer.'src="'.$url.'"></script>';
374
+ }
375
+ $bodyreplacementpayload = implode('',$arrscript);
376
+ }
377
+ $bodyreplacement = implode('',$this->move['first']);
378
+ $bodyreplacement .= $bodyreplacementpayload;
379
+ $bodyreplacement .= implode('',$this->move['last']);
380
+ $replaceTag = apply_filters( 'breeze_filter_js_replacetag', $replaceTag );
381
+
382
+ if($this->group_js == true){
383
+ if (strlen($this->jscode)>0) {
384
+ $this->inject_in_html($bodyreplacement,$replaceTag);
385
+ }
386
+ }else{
387
+ if(count($this->js_min_arr) > 0)
388
+ $this->inject_in_html($bodyreplacement,$replaceTag);
389
+ }
390
+
391
+
392
+
393
+ // restore comments
394
+ $this->content = $this->restore_comments($this->content);
395
+
396
+ // Restore IE hacks
397
+ $this->content = $this->restore_iehacks($this->content);
398
+
399
+ // Restore noptimize
400
+ $this->content = $this->restore_noptimize($this->content);
401
+
402
+ // Return the modified HTML
403
+ return $this->content;
404
+ }
405
+
406
+ // Checks against the white- and blacklists
407
+ private function ismergeable($tag) {
408
+ if (!empty($this->whitelist)) {
409
+ foreach ($this->whitelist as $match) {
410
+ if(strpos($tag,$match)!==false) {
411
+ return true;
412
+ }
413
+ }
414
+ // no match with whitelist
415
+ return false;
416
+ } else {
417
+ foreach($this->domove as $match) {
418
+ if(strpos($tag,$match)!==false) {
419
+ //Matched something
420
+ return false;
421
+ }
422
+ }
423
+
424
+ if ($this->movetolast($tag)) {
425
+ return false;
426
+ }
427
+
428
+ foreach($this->dontmove as $match) {
429
+ if(strpos($tag,$match)!==false) {
430
+ //Matched something
431
+ return false;
432
+ }
433
+ }
434
+
435
+ // If we're here it's safe to merge
436
+ return true;
437
+ }
438
+ }
439
+
440
+ //Checks agains the blacklist
441
+ private function ismovable($tag) {
442
+ if ($this->include_inline !== true || apply_filters('breeze_filter_js_unmovable',true)) {
443
+ return false;
444
+ }
445
+
446
+ foreach($this->domove as $match) {
447
+ if(strpos($tag,$match)!==false) {
448
+ //Matched something
449
+ return true;
450
+ }
451
+ }
452
+
453
+ if ($this->movetolast($tag)) {
454
+ return true;
455
+ }
456
+
457
+ foreach($this->dontmove as $match) {
458
+ if(strpos($tag,$match)!==false) {
459
+ //Matched something
460
+ return false;
461
+ }
462
+ }
463
+
464
+ //If we're here it's safe to move
465
+ return true;
466
+ }
467
+
468
+ private function movetolast($tag) {
469
+ foreach($this->domovelast as $match) {
470
+ if(strpos($tag,$match)!==false) {
471
+ //Matched, return true
472
+ return true;
473
+ }
474
+ }
475
+
476
+ //Should be in 'first'
477
+ return false;
478
+ }
479
+ /**
480
+ * Determines wheter a <script> $tag should be aggregated or not.
481
+ *
482
+ * We consider these as "aggregation-safe" currently:
483
+ * - script tags without a `type` attribute
484
+ * - script tags with an explicit `type` of `text/javascript`, 'text/ecmascript',
485
+ * 'application/javascript' or 'application/ecmascript'
486
+ *
487
+ * Everything else should return false.
488
+ *
489
+ * @param string $tag
490
+ * @return bool
491
+ *
492
+ * original function by https://github.com/zytzagoo/ on his AO fork, thanks Tomas!
493
+ */
494
+ public function should_aggregate($tag) {
495
+ preg_match('#<(script[^>]*)>#i',$tag,$scripttag);
496
+ if ( strpos($scripttag[1], 'type=')===false ) {
497
+ return true;
498
+ } else if ( preg_match('/type=["\']?(?:text|application)\/(?:javascript|ecmascript)["\']?/i', $scripttag[1]) ) {
499
+ return true;
500
+ } else {
501
+ return false;
502
+ }
503
+ }
504
+ }
inc/minification/breeze-minification-styles.php ADDED
@@ -0,0 +1,809 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Based on some work of autoptimize plugin
4
+ */
5
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
6
+
7
+ class Breeze_MinificationStyles extends Breeze_MinificationBase {
8
+ private $css = array();
9
+ private $csscode = array();
10
+ private $url = array();
11
+ private $restofcontent = '';
12
+ private $mhtml = '';
13
+ private $datauris = false;
14
+ private $hashmap = array();
15
+ private $alreadyminified = false;
16
+ private $inline = false;
17
+ private $defer = false;
18
+ private $defer_inline = false;
19
+ private $whitelist = '';
20
+ private $cssinlinesize = '';
21
+ private $cssremovables = array();
22
+ private $include_inline = false;
23
+ private $inject_min_late = '';
24
+ private $group_css = false;
25
+ private $custom_css_exclude = array();
26
+ private $css_group_val = array();
27
+ private $css_min_arr = array();
28
+ private $issetminfile = false;
29
+ private $url_group_arr = array();
30
+
31
+ //Reads the page and collects style tags
32
+ public function read($options) {
33
+ $noptimizeCSS = apply_filters( 'breeze_filter_css_noptimize', false, $this->content );
34
+ if ($noptimizeCSS) return false;
35
+
36
+ $whitelistCSS = apply_filters( 'breeze_filter_css_whitelist', '' );
37
+ if (!empty($whitelistCSS)) {
38
+ $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS)));
39
+ }
40
+
41
+ if ($options['nogooglefont'] == true) {
42
+ $removableCSS = "fonts.googleapis.com";
43
+ } else {
44
+ $removableCSS = "";
45
+ }
46
+ $removableCSS = apply_filters( 'breeze_filter_css_removables', $removableCSS);
47
+ if (!empty($removableCSS)) {
48
+ $this->cssremovables = array_filter(array_map('trim',explode(",",$removableCSS)));
49
+ }
50
+
51
+ $this->cssinlinesize = apply_filters('breeze_filter_css_inlinesize',256);
52
+
53
+ // filter to "late inject minified CSS", default to true for now (it is faster)
54
+ $this->inject_min_late = apply_filters('breeze_filter_css_inject_min_late',true);
55
+
56
+ // Remove everything that's not the header
57
+ if ( apply_filters('breeze_filter_css_justhead',$options['justhead']) == true ) {
58
+ $content = explode('</head>',$this->content,2);
59
+ $this->content = $content[0].'</head>';
60
+ $this->restofcontent = $content[1];
61
+ }
62
+
63
+ // include inline?
64
+ if( apply_filters('breeze_css_include_inline',$options['include_inline']) == true ) {
65
+ $this->include_inline = true;
66
+ }
67
+
68
+ // group css?
69
+ if( apply_filters('breeze_css_include_inline',$options['groupcss']) == true ) {
70
+ $this->group_css = true;
71
+ }
72
+ //custom js exclude
73
+ if(!empty($options['custom_css_exclude'])){
74
+ $this->custom_css_exclude = $options['custom_css_exclude'];
75
+ }
76
+ // what CSS shouldn't be autoptimized
77
+ $excludeCSS = $options['css_exclude'];
78
+ $excludeCSS = apply_filters( 'breeze_filter_css_exclude', $excludeCSS );
79
+ if ($excludeCSS!=="") {
80
+ $this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS)));
81
+ } else {
82
+ $this->dontmove = "";
83
+ }
84
+
85
+ // should we defer css?
86
+ // value: true/ false
87
+ $this->defer = $options['defer'];
88
+ $this->defer = apply_filters( 'breeze_filter_css_defer', $this->defer );
89
+
90
+ // should we inline while deferring?
91
+ // value: inlined CSS
92
+ $this->defer_inline = $options['defer_inline'];
93
+
94
+ // should we inline?
95
+ // value: true/ false
96
+ $this->inline = $options['inline'];
97
+ $this->inline = apply_filters( 'breeze_filter_css_inline', $this->inline );
98
+
99
+ // get cdn url
100
+ $this->cdn_url = $options['cdn_url'];
101
+
102
+ // Store data: URIs setting for later use
103
+ $this->datauris = $options['datauris'];
104
+
105
+ // noptimize me
106
+ $this->content = $this->hide_noptimize($this->content);
107
+
108
+ // exclude (no)script, as those may contain CSS which should be left as is
109
+ if ( strpos( $this->content, '<script' ) !== false ) {
110
+ $this->content = preg_replace_callback(
111
+ '#<(?:no)?script.*?<\/(?:no)?script>#is',
112
+ create_function(
113
+ '$matches',
114
+ 'return "%%SCRIPT".breeze_HASH."%%".base64_encode($matches[0])."%%SCRIPT%%";'
115
+ ),
116
+ $this->content
117
+ );
118
+ }
119
+
120
+ // Save IE hacks
121
+ $this->content = $this->hide_iehacks($this->content);
122
+
123
+ // hide comments
124
+ $this->content = $this->hide_comments($this->content);
125
+
126
+ // Get <style> and <link>
127
+ if(preg_match_all('#(<style[^>]*>.*</style>)|(<link[^>]*stylesheet[^>]*>)#Usmi',$this->content,$matches)) {
128
+ foreach($matches[0] as $tag) {
129
+ if ($this->isremovable($tag,$this->cssremovables)) {
130
+ $this->content = str_replace($tag,'',$this->content);
131
+ } else if ($this->ismovable($tag)) {
132
+ // Get the media
133
+ if(strpos($tag,'media=')!==false) {
134
+ preg_match('#media=(?:"|\')([^>]*)(?:"|\')#Ui',$tag,$medias);
135
+ $medias = explode(',',$medias[1]);
136
+ $media = array();
137
+ foreach($medias as $elem) {
138
+ if (empty($elem)) { $elem="all"; }
139
+ $media[] = $elem;
140
+ }
141
+ } else {
142
+ // No media specified - applies to all
143
+ $media = array('all');
144
+ }
145
+ $media = apply_filters( 'breeze_filter_css_tagmedia',$media,$tag );
146
+
147
+ if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
148
+ // <link>
149
+ $url = current(explode('?',$source[2],2));
150
+ //exclude css file
151
+ if(in_array($url,$this->custom_css_exclude)){
152
+ continue;
153
+ }
154
+
155
+ $path = $this->getpath($url);
156
+
157
+ if($path!==false && preg_match('#\.css$#',$path)) {
158
+ // Good link
159
+ $this->css[] = array($media,$path);
160
+ }else{
161
+ // Link is dynamic (.php etc)
162
+ $tag = '';
163
+ }
164
+ } else {
165
+ // inline css in style tags can be wrapped in comment tags, so restore comments
166
+ $tag = $this->restore_comments($tag);
167
+ preg_match('#<style.*>(.*)</style>#Usmi',$tag,$code);
168
+
169
+ // and re-hide them to be able to to the removal based on tag
170
+ $tag = $this->hide_comments($tag);
171
+
172
+ if ( $this->include_inline ) {
173
+ $code = preg_replace('#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm','$1',$code[1]);
174
+ $this->css[] = array($media,'INLINE;'.$code);
175
+ } else {
176
+ $tag = '';
177
+ }
178
+ }
179
+ // Remove the original style tag
180
+ $this->content = str_replace($tag,'',$this->content);
181
+ }
182
+ }
183
+ return true;
184
+ }
185
+ // Really, no styles?
186
+ return false;
187
+ }
188
+
189
+ // Joins and optimizes CSS
190
+ public function minify() {
191
+ foreach($this->css as $group) {
192
+ list($media,$css) = $group;
193
+ if(preg_match('#^INLINE;#',$css)) {
194
+ // <style>
195
+ $css = preg_replace('#^INLINE;#','',$css);
196
+ $css = $this->fixurls(ABSPATH.'/index.php',$css);
197
+ $tmpstyle = apply_filters( 'breeze_css_individual_style', $css, "" );
198
+ if ( has_filter('breeze_css_individual_style') && !empty($tmpstyle) ) {
199
+ $css=$tmpstyle;
200
+ $this->alreadyminified=true;
201
+ }
202
+ } else {
203
+ //<link>
204
+ if($css !== false && file_exists($css) && is_readable($css)) {
205
+ $cssPath = $css;
206
+ $css = $this->fixurls($cssPath,file_get_contents($cssPath));
207
+ $css = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$css);
208
+ $tmpstyle = apply_filters( 'breeze_css_individual_style', $css, $cssPath );
209
+ if (has_filter('breeze_css_individual_style') && !empty($tmpstyle)) {
210
+ $css=$tmpstyle;
211
+ $this->alreadyminified=true;
212
+ } else if ($this->can_inject_late($cssPath,$css)) {
213
+ $css="%%INJECTLATER".breeze_HASH."%%".base64_encode($cssPath)."|".md5($css)."%%INJECTLATER%%";
214
+ }
215
+ } else {
216
+ // Couldn't read CSS. Maybe getpath isn't working?
217
+ $css = '';
218
+ }
219
+ }
220
+ if($this->group_css == true){
221
+ foreach($media as $elem) {
222
+ if(!isset($this->csscode[$elem]))
223
+ $this->csscode[$elem] = '';
224
+ $this->csscode[$elem] .= "\n/*FILESTART*/".$css;
225
+ }
226
+ }else{
227
+ foreach ($media as $elem){
228
+ $this->css_group_val[] = $elem."_breezecssgroup_".$css;
229
+ }
230
+ }
231
+
232
+
233
+ }
234
+ if($this->group_css == true){
235
+ // Check for duplicate code
236
+ $md5list = array();
237
+ $tmpcss = $this->csscode;
238
+ foreach($tmpcss as $media => $code) {
239
+ $md5sum = md5($code);
240
+ $medianame = $media;
241
+ foreach($md5list as $med => $sum) {
242
+ // If same code
243
+ if($sum === $md5sum) {
244
+ //Add the merged code
245
+ $medianame = $med.', '.$media;
246
+ $this->csscode[$medianame] = $code;
247
+ $md5list[$medianame] = $md5list[$med];
248
+ unset($this->csscode[$med], $this->csscode[$media]);
249
+ unset($md5list[$med]);
250
+ }
251
+ }
252
+ $md5list[$medianame] = $md5sum;
253
+ }
254
+ unset($tmpcss);
255
+
256
+ // Manage @imports, while is for recursive import management
257
+ foreach ($this->csscode as &$thiscss) {
258
+ // Flag to trigger import reconstitution and var to hold external imports
259
+ $fiximports = false;
260
+ $external_imports = "";
261
+
262
+ while(preg_match_all('#^(/*\s?)@import.*(?:;|$)#Um',$thiscss,$matches)) {
263
+ foreach($matches[0] as $import) {
264
+ if ($this->isremovable($import,$this->cssremovables)) {
265
+ $thiscss = str_replace($import,'',$thiscss);
266
+ $import_ok = true;
267
+ } else {
268
+ $url = trim(preg_replace('#^.*((?:https?:|ftp:)?//.*\.css).*$#','$1',trim($import))," \t\n\r\0\x0B\"'");
269
+ $path = $this->getpath($url);
270
+ $import_ok = false;
271
+ if (file_exists($path) && is_readable($path)) {
272
+ $code = addcslashes($this->fixurls($path,file_get_contents($path)),"\\");
273
+ $code = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$code);
274
+ $tmpstyle = apply_filters( 'breeze_css_individual_style', $code, "" );
275
+ if ( has_filter('breeze_css_individual_style') && !empty($tmpstyle)) {
276
+ $code=$tmpstyle;
277
+ $this->alreadyminified=true;
278
+ } else if ($this->can_inject_late($path,$code)) {
279
+ $code="%%INJECTLATER".breeze_HASH."%%".base64_encode($path)."|".md5($code)."%%INJECTLATER%%";
280
+ }
281
+
282
+ if(!empty($code)) {
283
+ $tmp_thiscss = preg_replace('#(/\*FILESTART\*/.*)'.preg_quote($import,'#').'#Us','/*FILESTART2*/'.$code.'$1',$thiscss);
284
+ if (!empty($tmp_thiscss)) {
285
+ $thiscss = $tmp_thiscss;
286
+ $import_ok = true;
287
+ unset($tmp_thiscss);
288
+ }
289
+ unset($code);
290
+ }
291
+ }
292
+ }
293
+
294
+ if (!$import_ok) {
295
+ // external imports and general fall-back
296
+ $external_imports .= $import;
297
+ $thiscss = str_replace($import,'',$thiscss);
298
+ $fiximports = true;
299
+ }
300
+ }
301
+ $thiscss = preg_replace('#/\*FILESTART\*/#','',$thiscss);
302
+ $thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
303
+ }
304
+
305
+ // add external imports to top of aggregated CSS
306
+ if($fiximports) {
307
+ $thiscss=$external_imports.$thiscss;
308
+ }
309
+ }
310
+ unset($thiscss);
311
+
312
+ // $this->csscode has all the uncompressed code now.
313
+ $mhtmlcount = 0;
314
+ foreach($this->csscode as &$code) {
315
+ // Check for already-minified code
316
+ $hash = md5($code);
317
+ $ccheck = new Breeze_MinificationCache($hash,'css');
318
+ if($ccheck->check()) {
319
+ $code = $ccheck->retrieve();
320
+ $this->hashmap[md5($code)] = $hash;
321
+ continue;
322
+ }
323
+ unset($ccheck);
324
+
325
+ // Do the imaging!
326
+ $imgreplace = array();
327
+ preg_match_all('#(background[^;}]*url\((?!\s?"?\s?data)(.*)\)[^;}]*)(?:;|$|})#Usm',$code,$matches);
328
+
329
+ if(($this->datauris == true) && (function_exists('base64_encode')) && (is_array($matches))) {
330
+ foreach($matches[2] as $count => $quotedurl) {
331
+ $iurl = trim($quotedurl," \t\n\r\0\x0B\"'");
332
+
333
+ // if querystring, remove it from url
334
+ if (strpos($iurl,'?') !== false) { $iurl = strtok($iurl,'?'); }
335
+
336
+ $ipath = $this->getpath($iurl);
337
+
338
+ $datauri_max_size = 4096;
339
+ $datauri_max_size = (int) apply_filters( 'breeze_filter_css_datauri_maxsize', $datauri_max_size );
340
+ $datauri_exclude = apply_filters( 'breeze_filter_css_datauri_exclude', "");
341
+ if (!empty($datauri_exclude)) {
342
+ $no_datauris=array_filter(array_map('trim',explode(",",$datauri_exclude)));
343
+ foreach ($no_datauris as $no_datauri) {
344
+ if (strpos($iurl,$no_datauri)!==false) {
345
+ $ipath=false;
346
+ break;
347
+ }
348
+ }
349
+ }
350
+
351
+ if($ipath != false && preg_match('#\.(jpe?g|png|gif|bmp)$#i',$ipath) && file_exists($ipath) && is_readable($ipath) && filesize($ipath) <= $datauri_max_size) {
352
+ $ihash=md5($ipath);
353
+ $icheck = new Breeze_MinificationCache($ihash,'img');
354
+ if($icheck->check()) {
355
+ // we have the base64 image in cache
356
+ $headAndData=$icheck->retrieve();
357
+ $_base64data=explode(";base64,",$headAndData);
358
+ $base64data=$_base64data[1];
359
+ } else {
360
+ // It's an image and we don't have it in cache, get the type
361
+ $explA=explode('.',$ipath);
362
+ $type=end($explA);
363
+
364
+ switch($type) {
365
+ case 'jpeg':
366
+ $dataurihead = 'data:image/jpeg;base64,';
367
+ break;
368
+ case 'jpg':
369
+ $dataurihead = 'data:image/jpeg;base64,';
370
+ break;
371
+ case 'gif':
372
+ $dataurihead = 'data:image/gif;base64,';
373
+ break;
374
+ case 'png':
375
+ $dataurihead = 'data:image/png;base64,';
376
+ break;
377
+ case 'bmp':
378
+ $dataurihead = 'data:image/bmp;base64,';
379
+ break;
380
+ default:
381
+ $dataurihead = 'data:application/octet-stream;base64,';
382
+ }
383
+
384
+ // Encode the data
385
+ $base64data = base64_encode(file_get_contents($ipath));
386
+ $headAndData=$dataurihead.$base64data;
387
+
388
+ // Save in cache
389
+ $icheck->cache($headAndData,"text/plain");
390
+ }
391
+ unset($icheck);
392
+
393
+ // Add it to the list for replacement
394
+ $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$headAndData,$matches[1][$count]).";\n*".str_replace($quotedurl,'mhtml:%%MHTML%%!'.$mhtmlcount,$matches[1][$count]).";\n_".$matches[1][$count].';';
395
+
396
+ // Store image on the mhtml document
397
+ $this->mhtml .= "--_\r\nContent-Location:{$mhtmlcount}\r\nContent-Transfer-Encoding:base64\r\n\r\n{$base64data}\r\n";
398
+ $mhtmlcount++;
399
+ } else {
400
+ // just cdn the URL if applicable
401
+ if (!empty($this->cdn_url)) {
402
+ $url = trim($quotedurl," \t\n\r\0\x0B\"'");
403
+ $cdn_url=$this->url_replace_cdn($url);
404
+ $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$cdn_url,$matches[1][$count]);
405
+ }
406
+ }
407
+ }
408
+ } else if ((is_array($matches)) && (!empty($this->cdn_url))) {
409
+ // change background image urls to cdn-url
410
+ foreach($matches[2] as $count => $quotedurl) {
411
+ $url = trim($quotedurl," \t\n\r\0\x0B\"'");
412
+ $cdn_url=$this->url_replace_cdn($url);
413
+ $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$cdn_url,$matches[1][$count]);
414
+ }
415
+ }
416
+
417
+ if(!empty($imgreplace)) {
418
+ $code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code);
419
+ }
420
+
421
+ // CDN the fonts!
422
+ if ( (!empty($this->cdn_url)) && (apply_filters('breeze_filter_css_fonts_cdn',false)) && (version_compare(PHP_VERSION, '5.3.0') >= 0) ) {
423
+ $fontreplace = array();
424
+ include_once(BREEZE_PLUGIN_DIR.'inc/minification/config/minificationFontRegex.php');
425
+
426
+ preg_match_all($fonturl_regex,$code,$matches);
427
+ if (is_array($matches)) {
428
+ foreach($matches[8] as $count => $quotedurl) {
429
+ $url = trim($quotedurl," \t\n\r\0\x0B\"'");
430
+ $cdn_url=$this->url_replace_cdn($url);
431
+ $fontreplace[$matches[8][$count]] = str_replace($quotedurl,$cdn_url,$matches[8][$count]);
432
+ }
433
+ if(!empty($fontreplace)) {
434
+ $code = str_replace(array_keys($fontreplace),array_values($fontreplace),$code);
435
+ }
436
+ }
437
+ }
438
+
439
+ // Minify
440
+ if (($this->alreadyminified!==true) && (apply_filters( "breeze_css_do_minify", true))) {
441
+ if (class_exists('Minify_CSS_Compressor')) {
442
+ $tmp_code = trim(Minify_CSS_Compressor::process($code));
443
+ } else if(class_exists('CSSmin')) {
444
+ $cssmin = new CSSmin();
445
+ if (method_exists($cssmin,"run")) {
446
+ $tmp_code = trim($cssmin->run($code));
447
+ } elseif (@is_callable(array($cssmin,"minify"))) {
448
+ $tmp_code = trim(CssMin::minify($code));
449
+ }
450
+ }
451
+ if (!empty($tmp_code)) {
452
+ $code = $tmp_code;
453
+ unset($tmp_code);
454
+ }
455
+ }
456
+
457
+ $code = $this->inject_minified($code);
458
+
459
+ $tmp_code = apply_filters( 'breeze_css_after_minify',$code );
460
+ if (!empty($tmp_code)) {
461
+ $code = $tmp_code;
462
+ unset($tmp_code);
463
+ }
464
+
465
+ $this->hashmap[md5($code)] = $hash;
466
+ }
467
+ unset($code);
468
+ }else{
469
+ foreach ($this->css_group_val as $value){
470
+ $media = substr($value, 0 , strpos($value,'_breezecssgroup_'));
471
+ $css = substr($value,strpos($value,'_breezecssgroup_')+strlen('_breezecssgroup_'));
472
+
473
+ $hash = md5($css);
474
+ $ccheck = new Breeze_MinificationCache($hash,'css');
475
+ if($ccheck->check()) {
476
+ $css_exist = $ccheck->retrieve();
477
+ $this->css_min_arr[] = $media."_breezemedia_".$hash."_breezekey_".$css_exist;
478
+ continue;
479
+ }
480
+ unset($ccheck);
481
+
482
+ // Minify
483
+
484
+ if (class_exists('Minify_CSS_Compressor')) {
485
+ $tmp_code = trim(Minify_CSS_Compressor::process($css));
486
+ } else if(class_exists('CSSmin')) {
487
+ $cssmin = new CSSmin();
488
+ if (method_exists($cssmin,"run")) {
489
+ $tmp_code = trim($cssmin->run($css));
490
+ } elseif (@is_callable(array($cssmin,"minify"))) {
491
+ $tmp_code = trim(CssMin::minify($css));
492
+ }
493
+
494
+ }
495
+ if (!empty($tmp_code)) {
496
+ $css = $tmp_code;
497
+ unset($tmp_code);
498
+ }
499
+
500
+ $css = $this->inject_minified($css);
501
+ $css = apply_filters( 'breeze_css_after_minify',$css );
502
+ $this->css_min_arr[] = $media."_breezemedia_".$hash."_breezekey_".$css;
503
+ }
504
+ unset($css);
505
+
506
+ }
507
+ return true;
508
+ }
509
+
510
+ //Caches the CSS in uncompressed, deflated and gzipped form.
511
+ public function cache() {
512
+ if($this->datauris) {
513
+ // MHTML Preparation
514
+ $this->mhtml = "/*\r\nContent-Type: multipart/related; boundary=\"_\"\r\n\r\n".$this->mhtml."*/\r\n";
515
+ $md5 = md5($this->mhtml);
516
+ $cache = new Breeze_MinificationCache($md5,'txt');
517
+ if(!$cache->check()) {
518
+ // Cache our images for IE
519
+ $cache->cache($this->mhtml,'text/plain');
520
+ }
521
+ $mhtml = breeze_CACHE_URL.$cache->getname();
522
+ }
523
+ if($this->group_css == true){
524
+ // CSS cache
525
+ foreach($this->csscode as $media => $code) {
526
+ $md5 = $this->hashmap[md5($code)];
527
+
528
+ if($this->datauris) {
529
+ // Images for ie! Get the right url
530
+ $code = str_replace('%%MHTML%%',$mhtml,$code);
531
+ }
532
+
533
+ $cache = new Breeze_MinificationCache($md5,'css');
534
+ if(!$cache->check()) {
535
+ // Cache our code
536
+ $cache->cache($code,'text/css');
537
+ }
538
+ $this->url[$media] = breeze_CACHE_URL.$cache->getname();
539
+ }
540
+ }else{
541
+ foreach ($this->css_min_arr as $value){
542
+ $media = substr($value,0,strpos($value,'_breezemedia_'));
543
+ $code = substr($value,strpos($value,'_breezemedia_')+strlen('_breezemedia_'));
544
+ $hash = substr($code,0,strpos($code,'_breezekey_'));
545
+ $css = substr($code,strpos($code,'_breezekey_')+strlen('_breezekey_'));
546
+
547
+ $cache = new Breeze_MinificationCache($hash,'css');
548
+ if(!$cache->check()) {
549
+ // Cache our code
550
+ $cache->cache($css,'text/css');
551
+ }
552
+ $this->url_group_arr[] = $media."_breezemedia_".$hash."_breezekey_".breeze_CACHE_URL.$cache->getname();
553
+ }
554
+ }
555
+ }
556
+
557
+ //Returns the content
558
+ public function getcontent() {
559
+ // restore IE hacks
560
+ $this->content = $this->restore_iehacks($this->content);
561
+
562
+ // restore comments
563
+ $this->content = $this->restore_comments($this->content);
564
+
565
+ // restore (no)script
566
+ if ( strpos( $this->content, '%%SCRIPT%%' ) !== false ) {
567
+ $this->content = preg_replace_callback(
568
+ '#%%SCRIPT'.breeze_HASH.'%%(.*?)%%SCRIPT%%#is',
569
+ create_function(
570
+ '$matches',
571
+ 'return base64_decode($matches[1]);'
572
+ ),
573
+ $this->content
574
+ );
575
+ }
576
+
577
+ // restore noptimize
578
+ $this->content = $this->restore_noptimize($this->content);
579
+
580
+ //Restore the full content
581
+ if(!empty($this->restofcontent)) {
582
+ $this->content .= $this->restofcontent;
583
+ $this->restofcontent = '';
584
+ }
585
+
586
+ // Inject the new stylesheets
587
+ $replaceTag = array("<title","before");
588
+ $replaceTag = apply_filters( 'breeze_filter_css_replacetag', $replaceTag );
589
+ if($this->group_css == true){
590
+ if ($this->inline == true) {
591
+ foreach($this->csscode as $media => $code) {
592
+ $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$code.'</style>',$replaceTag);
593
+ }
594
+ } else {
595
+ if ($this->defer == true) {
596
+ $deferredCssBlock = "<script data-cfasync='false'>function lCss(url,media) {var d=document;var l=d.createElement('link');l.rel='stylesheet';l.type='text/css';l.href=url;l.media=media;aoin=d.getElementsByTagName('noscript')[0];aoin.parentNode.insertBefore(l,aoin.nextSibling);}function deferredCSS() {";
597
+ $noScriptCssBlock = "<noscript>";
598
+ $defer_inline_code=$this->defer_inline;
599
+ $defer_inline_code=apply_filters( 'breeze_filter_css_defer_inline', $defer_inline_code );
600
+ if(!empty($defer_inline_code)){
601
+
602
+ $iCssHash=md5($defer_inline_code);
603
+ $iCssCache = new Breeze_MinificationCache($iCssHash,'css');
604
+ if($iCssCache->check()) {
605
+ // we have the optimized inline CSS in cache
606
+ $defer_inline_code=$iCssCache->retrieve();
607
+ } else {
608
+ if (class_exists('Minify_CSS_Compressor')) {
609
+ $tmp_code = trim(Minify_CSS_Compressor::process($this->defer_inline));
610
+ } else if(class_exists('CSSmin')) {
611
+ $cssmin = new CSSmin();
612
+ $tmp_code = trim($cssmin->run($defer_inline_code));
613
+ }
614
+
615
+ if (!empty($tmp_code)) {
616
+ $defer_inline_code = $tmp_code;
617
+ $iCssCache->cache($defer_inline_code,"text/css");
618
+ unset($tmp_code);
619
+ }
620
+ }
621
+ $code_out='<style type="text/css" id="aoatfcss" media="all">'.$defer_inline_code.'</style>';
622
+ $this->inject_in_html($code_out,$replaceTag);
623
+ }
624
+ }
625
+
626
+ foreach($this->url as $media => $url) {
627
+ $url = $this->url_replace_cdn($url);
628
+
629
+ //Add the stylesheet either deferred (import at bottom) or normal links in head
630
+ if($this->defer == true) {
631
+ $deferredCssBlock .= "lCss('".$url."','".$media."');";
632
+ $noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />';
633
+ } else {
634
+ if (strlen($this->csscode[$media]) > $this->cssinlinesize) {
635
+ $this->inject_in_html('<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />',$replaceTag);
636
+ } else if (strlen($this->csscode[$media])>0) {
637
+ $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$this->csscode[$media].'</style>',$replaceTag);
638
+ }
639
+ }
640
+ }
641
+
642
+ if($this->defer == true) {
643
+ $deferredCssBlock .= "}if(window.addEventListener){window.addEventListener('DOMContentLoaded',deferredCSS,false);}else{window.onload = deferredCSS;}</script>";
644
+ $noScriptCssBlock .= "</noscript>";
645
+ $this->inject_in_html($noScriptCssBlock,$replaceTag);
646
+ $this->inject_in_html($deferredCssBlock,array('</body>','before'));
647
+ }
648
+ }
649
+ }else{
650
+
651
+ if ($this->inline == true) {
652
+ foreach($this->csscode as $media => $code) {
653
+ $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$code.'</style>',$replaceTag);
654
+ }
655
+ } else {
656
+ if ($this->defer == true) {
657
+ $deferredCssBlock = "<script data-cfasync='false'>function lCss(url,media) {var d=document;var l=d.createElement('link');l.rel='stylesheet';l.type='text/css';l.href=url;l.media=media;aoin=d.getElementsByTagName('noscript')[0];aoin.parentNode.insertBefore(l,aoin.nextSibling);}function deferredCSS() {";
658
+ $noScriptCssBlock = "<noscript>";
659
+ $defer_inline_code=$this->defer_inline;
660
+ $defer_inline_code=apply_filters( 'breeze_filter_css_defer_inline', $defer_inline_code );
661
+ if(!empty($defer_inline_code)){
662
+
663
+ $iCssHash=md5($defer_inline_code);
664
+ $iCssCache = new Breeze_MinificationCache($iCssHash,'css');
665
+ if($iCssCache->check()) {
666
+ // we have the optimized inline CSS in cache
667
+ $defer_inline_code=$iCssCache->retrieve();
668
+ } else {
669
+ if (class_exists('Minify_CSS_Compressor')) {
670
+ $tmp_code = trim(Minify_CSS_Compressor::process($this->defer_inline));
671
+ } else if(class_exists('CSSmin')) {
672
+ $cssmin = new CSSmin();
673
+ $tmp_code = trim($cssmin->run($defer_inline_code));
674
+ }
675
+
676
+ if (!empty($tmp_code)) {
677
+ $defer_inline_code = $tmp_code;
678
+ $iCssCache->cache($defer_inline_code,"text/css");
679
+ unset($tmp_code);
680
+ }
681
+ }
682
+ $code_out='<style type="text/css" id="aoatfcss" media="all">'.$defer_inline_code.'</style>';
683
+ $this->inject_in_html($code_out,$replaceTag);
684
+ }
685
+ }
686
+
687
+ foreach ($this->url_group_arr as $value){
688
+ $media = substr($value,0,strpos($value,'_breezemedia_'));
689
+ $code = substr($value,strpos($value,'_breezemedia_')+strlen('_breezemedia_'));
690
+ $hash = substr($code,0,strpos($code,'_breezekey_'));
691
+ $url = substr($code,strpos($code,'_breezekey_')+strlen('_breezekey_'));
692
+
693
+ $cache = new Breeze_MinificationCache($hash,'css');
694
+ if($cache->check()) {
695
+ $csscode = $cache->retrieve();
696
+ }
697
+ //Add the stylesheet either deferred (import at bottom) or normal links in head
698
+ if($this->defer == true) {
699
+ $deferredCssBlock .= "lCss('".$url."','".$media."');";
700
+ $noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />';
701
+ } else {
702
+ if (strlen($csscode) > $this->cssinlinesize) {
703
+ $url = $this->url_replace_cdn($url);
704
+ $this->inject_in_html('<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />',$replaceTag);
705
+ } else if (strlen($csscode)>0) {
706
+ $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$csscode.'</style>',$replaceTag);
707
+ }
708
+ }
709
+ }
710
+
711
+ if($this->defer == true) {
712
+ $deferredCssBlock .= "}if(window.addEventListener){window.addEventListener('DOMContentLoaded',deferredCSS,false);}else{window.onload = deferredCSS;}</script>";
713
+ $noScriptCssBlock .= "</noscript>";
714
+ $this->inject_in_html($noScriptCssBlock,$replaceTag);
715
+ $this->inject_in_html($deferredCssBlock,array('</body>','before'));
716
+ }
717
+ }
718
+ }
719
+
720
+
721
+ //Return the modified stylesheet
722
+ return $this->content;
723
+ }
724
+
725
+ static function fixurls($file,$code) {
726
+ $file = str_replace(BREEZE_ROOT_DIR,'/',$file);
727
+ $dir = dirname($file); //Like /wp-content
728
+
729
+ // quick fix for import-troubles in e.g. arras theme
730
+ $code=preg_replace('#@import ("|\')(.+?)\.css("|\')#','@import url("${2}.css")',$code);
731
+
732
+ if(preg_match_all('#url\((?!data)(?!\#)(?!"\#)(.*)\)#Usi',$code,$matches)) {
733
+ $replace = array();
734
+ foreach($matches[1] as $k => $url) {
735
+ // Remove quotes
736
+ $url = trim($url," \t\n\r\0\x0B\"'");
737
+ $noQurl = trim($url,"\"'");
738
+ if ($url!==$noQurl) {
739
+ $removedQuotes=true;
740
+ } else {
741
+ $removedQuotes=false;
742
+ }
743
+ $url=$noQurl;
744
+ if(substr($url,0,1)=='/' || preg_match('#^(https?://|ftp://|data:)#i',$url)) {
745
+ //URL is absolute
746
+ continue;
747
+ } else {
748
+ // relative URL
749
+ $newurl = preg_replace('/https?:/','',str_replace(" ","%20",breeze_WP_ROOT_URL.str_replace('//','/',$dir.'/'.$url)));
750
+
751
+ $hash = md5($url);
752
+ $code = str_replace($matches[0][$k],$hash,$code);
753
+
754
+ if (!empty($removedQuotes)) {
755
+ $replace[$hash] = 'url(\''.$newurl.'\')';
756
+ } else {
757
+ $replace[$hash] = 'url('.$newurl.')';
758
+ }
759
+ }
760
+ }
761
+ //Do the replacing here to avoid breaking URLs
762
+ $code = str_replace(array_keys($replace),array_values($replace),$code);
763
+ }
764
+ return $code;
765
+ }
766
+
767
+ private function ismovable($tag) {
768
+ if (!empty($this->whitelist)) {
769
+ foreach ($this->whitelist as $match) {
770
+ if(strpos($tag,$match)!==false) {
771
+ return true;
772
+ }
773
+ }
774
+ // no match with whitelist
775
+ return false;
776
+ } else {
777
+ if (is_array($this->dontmove)) {
778
+ foreach($this->dontmove as $match) {
779
+ if(strpos($tag,$match)!==false) {
780
+ //Matched something
781
+ return false;
782
+ }
783
+ }
784
+ }
785
+
786
+ //If we're here it's safe to move
787
+ return true;
788
+ }
789
+ }
790
+
791
+ private function can_inject_late($cssPath,$css) {
792
+ if ((strpos($cssPath,"min.css")===false) || ($this->inject_min_late!==true)) {
793
+ // late-inject turned off or file not minified based on filename
794
+ return false;
795
+ } else if (strpos($css,"@import")!==false) {
796
+ // can't late-inject files with imports as those need to be aggregated
797
+ return false;
798
+ } else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("breeze_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) {
799
+ // don't late-inject CSS with font-src's if fonts are set to be CDN'ed
800
+ return false;
801
+ } else if ( (($this->datauris == true) || (!empty($this->cdn_url))) && preg_match("#background[^;}]*url\(#Ui",$css) ) {
802
+ // don't late-inject CSS with images if CDN is set OR is image inlining is on
803
+ return false;
804
+ } else {
805
+ // phew, all is safe, we can late-inject
806
+ return true;
807
+ }
808
+ }
809
+ }
inc/minification/breeze-minify-main.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @copyright 2017 Cloudways https://www.cloudways.com
4
+ *
5
+ * Original development of this plugin by JoomUnited https://www.joomunited.com/
6
+ *
7
+ * This program is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+ defined('ABSPATH') || die('No direct script access allowed!');
22
+
23
+ class Breeze_Minify {
24
+
25
+ public function __construct()
26
+ {
27
+ //check disable cache for page
28
+ $domain = (((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS']!=='off') || $_SERVER['SERVER_PORT']==443) ? 'https://':'http://' ).$_SERVER['HTTP_HOST'];
29
+ $current_url = $domain.$_SERVER['REQUEST_URI'];
30
+
31
+ $check_url = $this->check_exclude_url($current_url);
32
+
33
+ //load config file when redirect template
34
+ if (!in_array($current_url, $check_url)) {
35
+ //cache html
36
+ //cache minification
37
+ if (Breeze_MinificationCache::create_cache_minification_folder()) {
38
+ $conf = get_option('breeze_basic_settings');
39
+ if ( !empty($conf['breeze-minify-html']) || !empty($conf['breeze-minify-css']) || !empty($conf['breeze-minify-js']) ) {
40
+ if (defined('breeze_INIT_EARLIER')) {
41
+ add_action('init', array($this,'breeze_start_buffering'), -1);
42
+ } else {
43
+ add_action('template_redirect', array($this,'breeze_start_buffering'), 2);
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ }
50
+ /*
51
+ * Start buffer
52
+ */
53
+ public function breeze_start_buffering(){
54
+ $ao_noptimize = false;
55
+
56
+ // check for DONOTMINIFY constant as used by e.g. WooCommerce POS
57
+ if (defined('DONOTMINIFY') && (constant('DONOTMINIFY') === true || constant('DONOTMINIFY') === "true")) {
58
+ $ao_noptimize = true;
59
+ }
60
+ // filter you can use to block autoptimization on your own terms
61
+ $ao_noptimize = (bool) apply_filters('breeze_filter_noptimize', $ao_noptimize);
62
+ if (!is_feed() && !$ao_noptimize && !is_admin()) {
63
+ // Config element
64
+ $conf = get_option('breeze_basic_settings');
65
+ // Load our base class
66
+ include_once(BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-base.php');
67
+
68
+ // Load extra classes and set some vars
69
+ if (!empty($conf['breeze-minify-html'])) {
70
+ include_once(BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-html.php');
71
+ // BUG: new minify-html does not support keeping HTML comments, skipping for now
72
+ if(!class_exists('Minify_HTML')){
73
+ @include(BREEZE_PLUGIN_DIR . 'inc/minification/minify/minify-html.php');
74
+ }
75
+ }
76
+
77
+ if (!empty($conf['breeze-minify-js'])) {
78
+ include_once(BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-scripts.php');
79
+ if (!class_exists('JSMin')) {
80
+ if (defined('breeze_LEGACY_MINIFIERS')) {
81
+ @include(BREEZE_PLUGIN_DIR . 'inc/minification/minify/jsmin-1.1.1.php');
82
+ } else {
83
+ @include(BREEZE_PLUGIN_DIR . 'inc/minification/minify/minify-2.1.7-jsmin.php');
84
+ }
85
+ }
86
+ if (!defined('CONCATENATE_SCRIPTS')) {
87
+ define('CONCATENATE_SCRIPTS', false);
88
+ }
89
+ if (!defined('COMPRESS_SCRIPTS')) {
90
+ define('COMPRESS_SCRIPTS', false);
91
+ }
92
+ }
93
+ if (!empty($conf['breeze-minify-css'])) {
94
+ include_once(BREEZE_PLUGIN_DIR . 'inc/minification/breeze-minification-styles.php');
95
+ if (defined('breeze_LEGACY_MINIFIERS')) {
96
+ if (!class_exists('Minify_CSS_Compressor')) {
97
+ @include(BREEZE_PLUGIN_DIR . 'inc/minification/minify/minify-css-compressor.php');
98
+ }
99
+ } else {
100
+ if (!class_exists('CSSmin')) {
101
+ @include(BREEZE_PLUGIN_DIR . 'inc/minification/minify/yui-php-cssmin-2.4.8-4_fgo.php');
102
+ }
103
+ }
104
+ if (!defined('COMPRESS_CSS')) {
105
+ define('COMPRESS_CSS', false);
106
+ }
107
+ }
108
+ // Now, start the real thing!
109
+ add_filter('breeze_minify_content_return',array($this,'breeze_end_buffering'));
110
+ }
111
+ }
112
+
113
+ /*
114
+ * Minify css , js and optimize html when start
115
+ */
116
+
117
+ public function breeze_end_buffering($content) {
118
+ if (stripos($content, "<html") === false || stripos($content, "<html amp") !== false || stripos($content, "<html ⚡") !== false || stripos($content, "<xsl:stylesheet") !== false) {
119
+ return $content;
120
+ }
121
+ // load URL constants as late as possible to allow domain mapper to kick in
122
+ if (function_exists("domain_mapping_siteurl")) {
123
+ define('breeze_WP_SITE_URL', domain_mapping_siteurl(get_current_blog_id()));
124
+ define('breeze_WP_CONTENT_URL', str_replace(get_original_url(breeze_WP_SITE_URL), breeze_WP_SITE_URL, content_url()));
125
+ } else {
126
+ define('breeze_WP_SITE_URL', site_url());
127
+ define('breeze_WP_CONTENT_URL', content_url());
128
+ }
129
+ if (is_multisite() && apply_filters('breeze_separate_blog_caches', true)) {
130
+ $blog_id = get_current_blog_id();
131
+ define('breeze_CACHE_URL', breeze_WP_CONTENT_URL . BREEZE_CACHE_CHILD_DIR . $blog_id . '/');
132
+ } else {
133
+ define('breeze_CACHE_URL', breeze_WP_CONTENT_URL . BREEZE_CACHE_CHILD_DIR);
134
+ }
135
+ define('breeze_WP_ROOT_URL', str_replace(BREEZE_WP_CONTENT_NAME, '', breeze_WP_CONTENT_URL));
136
+
137
+ define('breeze_HASH',wp_hash(breeze_CACHE_URL));
138
+ // Config element
139
+ $conf = get_option('breeze_basic_settings');
140
+ $minify = get_option('breeze_advanced_settings');
141
+
142
+ // Choose the classes
143
+ $classes = array();
144
+ if (!empty($conf['breeze-minify-js']))
145
+ $classes[] = 'Breeze_MinificationScripts';
146
+ if (!empty($conf['breeze-minify-css']))
147
+ $classes[] = 'Breeze_MinificationStyles';
148
+ if (!empty($conf['breeze-minify-html']))
149
+ $classes[] = 'Breeze_MinificationHtml';
150
+ $groupcss = false;
151
+ $groupjs = false;
152
+ if (!empty($minify['breeze-group-css'])){
153
+ $groupcss = true;
154
+ }
155
+ if (!empty($minify['breeze-group-js'])){
156
+ $groupjs = true;
157
+ }
158
+ // Set some options
159
+ $classoptions = array(
160
+ 'Breeze_MinificationScripts' => array(
161
+ 'justhead' => false,
162
+ 'forcehead' => false,
163
+ 'trycatch' => false,
164
+ 'js_exclude' => "s_sid, smowtion_size, sc_project, WAU_, wau_add, comment-form-quicktags, edToolbar, ch_client, seal.js",
165
+ 'cdn_url' => "",
166
+ 'include_inline' => true,
167
+ 'group_js' => $groupjs,
168
+ 'custom_js_exclude' => $minify['breeze-exclude-js']
169
+ ),
170
+ 'Breeze_MinificationStyles' => array(
171
+ 'justhead' => false,
172
+ 'datauris' => false,
173
+ 'defer' => false,
174
+ 'defer_inline' => false,
175
+ 'inline' => false,
176
+ 'css_exclude' => "admin-bar.min.css, dashicons.min.css",
177
+ 'cdn_url' => "",
178
+ 'include_inline' => true,
179
+ 'nogooglefont' => false,
180
+ 'groupcss' => $groupcss,
181
+ 'custom_css_exclude' => $minify['breeze-exclude-css']
182
+ ),
183
+ 'Breeze_MinificationHtml' => array(
184
+ 'keepcomments' => false
185
+ )
186
+ );
187
+
188
+ $content = apply_filters('breeze_filter_html_before_minify', $content);
189
+
190
+ if (!empty($conf) && $conf['breeze-disable-admin'] && current_user_can('manage_options')) {
191
+ $content = apply_filters('breeze_html_after_minify', $content);
192
+ }else{
193
+ // Run the classes
194
+ foreach ($classes as $name) {
195
+ $instance = new $name($content);
196
+
197
+ if ($instance->read($classoptions[$name])) {
198
+ $instance->minify();
199
+ $instance->cache();
200
+ $content = $instance->getcontent();
201
+ }
202
+ unset($instance);
203
+ }
204
+ $content = apply_filters('breeze_html_after_minify', $content);
205
+ }
206
+ return $content;
207
+ }
208
+ /*
209
+ * check url from Never cache the following pages area
210
+ */
211
+ public function check_exclude_url($current_url){
212
+
213
+ $check_url = array();
214
+ $opts_config = get_option('breeze_advanced_settings');
215
+ if (!empty($opts_config['breeze-exclude-urls'])) {
216
+ foreach ($opts_config['breeze-exclude-urls'] as $v) {
217
+ if ($v && $v[0] == '/') {
218
+ //part url
219
+ if (preg_match('[\*]', $v) == 1) {
220
+ $v = substr($v, 0, strpos($v, "/(.*)"));
221
+ if(!empty($v)){
222
+ if(strpos($current_url,$v) != false){
223
+ $check_url[] = $current_url;
224
+ }
225
+ }
226
+ } else {
227
+ if(!empty($v)){
228
+ if(strpos($current_url,$v) != false){
229
+ $url = $current_url;
230
+ }else{
231
+ $url = BREEZE_SITEURL . $v;
232
+ }
233
+ }
234
+ $check_url[] = $url;
235
+ }
236
+ } else {
237
+ if(preg_match('[\*]', $v) == 1){
238
+ $v = substr($v, 0, strpos($v, "&(.*)"));
239
+ $v = substr($v, -10);
240
+ if(!empty($v)){
241
+ if(strpos($current_url,$v) != false){
242
+ $check_url[] = $current_url;
243
+ }
244
+ }
245
+ }else{
246
+ //full url
247
+ $check_url[] = $v;
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ return $check_url;
254
+ }
255
+ }
inc/minification/config/default.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php exit;
2
+
3
+ //Check everything exists before using it
4
+ if(!isset($_SERVER['HTTP_ACCEPT_ENCODING']))
5
+ $_SERVER['HTTP_ACCEPT_ENCODING'] = '';
6
+ if(!isset($_SERVER['HTTP_USER_AGENT']))
7
+ $_SERVER['HTTP_USER_AGENT'] = '';
8
+
9
+ // Determine supported compression method
10
+ $gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
11
+ $deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
12
+
13
+ // Determine used compression method
14
+ $encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none');
15
+
16
+ // Check for buggy versions of Internet Explorer
17
+ if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') &&
18
+ preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches))
19
+ {
20
+ $version = floatval($matches[1]);
21
+
22
+ if ($version < 6)
23
+ $encoding = 'none';
24
+
25
+ if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1'))
26
+ $encoding = 'none';
27
+ }
28
+
29
+ //Some servers compress the output of PHP - Don't break in those cases
30
+ if(ini_get('output_handler') == 'ob_gzhandler' || ini_get('zlib.output_compression') == 1)
31
+ $encoding = 'none';
32
+
33
+ //Get data
34
+ $contents = file_get_contents(__FILE__.'.'.$encoding);
35
+
36
+ // first check if we have to send 304
37
+ $eTag=md5($contents);
38
+ $modTime=filemtime(__FILE__.'.none');
39
+
40
+ $eTagMatch = (isset($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'],$eTag));
41
+ $modTimeMatch = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modTime);
42
+
43
+ if (($modTimeMatch)||($eTagMatch)) {
44
+ header('HTTP/1.1 304 Not Modified');
45
+ header('Connection: close');
46
+ } else {
47
+ // send all sorts of headers
48
+ $expireTime=60*60*24*356; // 1y max according to RFC
49
+
50
+ if(isset($encoding) && $encoding != 'none')
51
+ {
52
+ header('Content-Encoding: '.$encoding);
53
+ }
54
+ header('Vary: Accept-Encoding');
55
+ header('Content-Length: '.strlen($contents));
56
+ header('Content-type: %%CONTENT%%; charset=utf-8');
57
+ header('Cache-Control: max-age='.$expireTime.', public, must-revalidate');
58
+ header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT'); //10 years
59
+ header('ETag: ' . $eTag);
60
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
61
+
62
+ // send output
63
+ echo $contents;
64
+ }
inc/minification/config/delayed.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php exit;
2
+
3
+ //Check everything exists before using it
4
+ if(!isset($_SERVER['HTTP_ACCEPT_ENCODING']))
5
+ $_SERVER['HTTP_ACCEPT_ENCODING'] = '';
6
+ if(!isset($_SERVER['HTTP_USER_AGENT']))
7
+ $_SERVER['HTTP_USER_AGENT'] = '';
8
+
9
+ // Determine supported compression method
10
+ $gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
11
+ $deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
12
+
13
+ // Determine used compression method
14
+ $encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none');
15
+
16
+ // Check for buggy versions of Internet Explorer
17
+ if (!strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') &&
18
+ preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches))
19
+ {
20
+ $version = floatval($matches[1]);
21
+
22
+ if ($version < 6)
23
+ $encoding = 'none';
24
+
25
+ if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1'))
26
+ $encoding = 'none';
27
+ }
28
+
29
+ //Some servers compress the output of PHP - Don't break in those cases
30
+ if(ini_get('output_handler') == 'ob_gzhandler' || ini_get('zlib.output_compression') == 1)
31
+ $encoding = 'none';
32
+
33
+ $iscompressed = file_exists(__FILE__.'.'.$encoding);
34
+ if($encoding != 'none' && $iscompressed == false)
35
+ {
36
+ $flag = ($encoding == 'gzip' ? FORCE_GZIP : FORCE_DEFLATE);
37
+ $code = file_get_contents(__FILE__.'.none');
38
+ $contents = gzencode($code,9,$flag);
39
+ }else{
40
+ //Get data
41
+ $contents = file_get_contents(__FILE__.'.'.$encoding);
42
+ }
43
+
44
+ // first check if we have to send 304
45
+ // inspired by http://www.jonasjohn.de/snippets/php/caching.htm
46
+
47
+ $eTag=md5($contents);
48
+ $modTime=filemtime(__FILE__.'.none');
49
+
50
+ $eTagMatch = (isset($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'],$eTag));
51
+ $modTimeMatch = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modTime);
52
+
53
+ if (($modTimeMatch)||($eTagMatch)) {
54
+ header('HTTP/1.1 304 Not Modified');
55
+ header('Connection: close');
56
+ } else {
57
+ // send all sorts of headers
58
+ $expireTime=60*60*24*355; // 1y max according to RFC
59
+ if ($encoding != 'none') {
60
+ header('Content-Encoding: '.$encoding);
61
+ }
62
+ header('Vary: Accept-Encoding');
63
+ header('Content-Length: '.strlen($contents));
64
+ header('Content-type: %%CONTENT%%; charset=utf-8');
65
+ header('Cache-Control: max-age='.$expireTime.', public, must-revalidate');
66
+ header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expireTime).' GMT');
67
+ header('ETag: ' . $eTag);
68
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s', $modTime).' GMT');
69
+
70
+ // send output
71
+ echo $contents;
72
+
73
+ //And write to filesystem cache if not done yet
74
+ if($encoding != 'none' && $iscompressed == false)
75
+ {
76
+ //Write the content we sent
77
+ file_put_contents(__FILE__.'.'.$encoding,$contents);
78
+
79
+ //And write the new content
80
+ $flag = ($encoding == 'gzip' ? FORCE_DEFLATE : FORCE_GZIP);
81
+ $ext = ($encoding == 'gzip' ? 'deflate' : 'gzip');
82
+ $contents = gzencode($code,9,$flag);
83
+ file_put_contents(__FILE__.'.'.$ext,$contents);
84
+ }
85
+ }
inc/minification/config/minificationFontRegex.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // regex to find fonts, externalised to avoid nasty errors for php<5.3
3
+
4
+ $fonturl_regex = <<<'LOD'
5
+ ~(?(DEFINE)(?<quoted_content>(["']) (?>[^"'\\]++ | \\{2} | \\. | (?!\g{-1})["'] )*+ \g{-1})(?<comment> /\* .*? \*/ ) (?<url_skip>(?: data: ) [^"'\s)}]*+ ) (?<other_content>(?> [^u}/"']++ | \g<quoted_content> | \g<comment> | \Bu | u(?!rl\s*+\() | /(?!\*) | \g<url_start> \g<url_skip> ["']?+ )++ ) (?<anchor> \G(?<!^) ["']?+ | @font-face \s*+ { ) (?<url_start> url\( \s*+ ["']?+ ) ) \g<comment> (*SKIP)(*FAIL) | \g<anchor> \g<other_content>?+ \g<url_start> \K ((?:(?:https?:)?(?://[[:alnum:]\-\.]+)(?::[0-9]+)?)?\/[^"'\s)}]*+) ~xs
6
+ LOD;
7
+
8
+ ?>
inc/minification/minify/index.html ADDED
@@ -0,0 +1 @@
 
1
+ <html><head><meta name="robots" content="noindex, nofollow"></head><body>Generated by <a href="http://wordpress.org/extend/plugins/autoptimize/" rel="nofollow">Autoptimize</a></body></html>
inc/minification/minify/jsmin-1.1.1.php ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * jsmin.php - PHP implementation of Douglas Crockford's JSMin.
4
+ *
5
+ * This is pretty much a direct port of jsmin.c to PHP with just a few
6
+ * PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
7
+ * outputs to stdout, this library accepts a string as input and returns another
8
+ * string as output.
9
+ *
10
+ * PHP 5 or higher is required.
11
+ *
12
+ * Permission is hereby granted to use this version of the library under the
13
+ * same terms as jsmin.c, which has the following license:
14
+ *
15
+ * --
16
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
17
+ *
18
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
19
+ * this software and associated documentation files (the "Software"), to deal in
20
+ * the Software without restriction, including without limitation the rights to
21
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
22
+ * of the Software, and to permit persons to whom the Software is furnished to do
23
+ * so, subject to the following conditions:
24
+ *
25
+ * The above copyright notice and this permission notice shall be included in all
26
+ * copies or substantial portions of the Software.
27
+ *
28
+ * The Software shall be used for Good, not Evil.
29
+ *
30
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36
+ * SOFTWARE.
37
+ * --
38
+ *
39
+ * @package JSMin
40
+ * @author Ryan Grove <ryan@wonko.com>
41
+ * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
42
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
43
+ * @license http://opensource.org/licenses/mit-license.php MIT License
44
+ * @version 1.1.1 (2008-03-02)
45
+ * @link http://code.google.com/p/jsmin-php/
46
+ */
47
+
48
+ class JSMin {
49
+ const ORD_LF = 10;
50
+ const ORD_SPACE = 32;
51
+
52
+ protected $a = '';
53
+ protected $b = '';
54
+ protected $input = '';
55
+ protected $inputIndex = 0;
56
+ protected $inputLength = 0;
57
+ protected $lookAhead = null;
58
+ protected $output = '';
59
+
60
+ // -- Public Static Methods --------------------------------------------------
61
+
62
+ public static function minify($js) {
63
+ $jsmin = new JSMin($js);
64
+ return $jsmin->min();
65
+ }
66
+
67
+ // -- Public Instance Methods ------------------------------------------------
68
+
69
+ public function __construct($input) {
70
+ $this->input = str_replace("\r\n", "\n", $input);
71
+ $this->inputLength = strlen($this->input);
72
+ }
73
+
74
+ // -- Protected Instance Methods ---------------------------------------------
75
+
76
+ protected function action($d) {
77
+ switch($d) {
78
+ case 1:
79
+ $this->output .= $this->a;
80
+
81
+ case 2:
82
+ $this->a = $this->b;
83
+
84
+ if ($this->a === "'" || $this->a === '"') {
85
+ for (;;) {
86
+ $this->output .= $this->a;
87
+ $this->a = $this->get();
88
+
89
+ if ($this->a === $this->b) {
90
+ break;
91
+ }
92
+
93
+ if (ord($this->a) <= self::ORD_LF) {
94
+ throw new JSMinException('Unterminated string literal.');
95
+ }
96
+
97
+ if ($this->a === '\\') {
98
+ $this->output .= $this->a;
99
+ $this->a = $this->get();
100
+ }
101
+ }
102
+ }
103
+
104
+ case 3:
105
+ $this->b = $this->next();
106
+
107
+ if ($this->b === '/' && (
108
+ $this->a === '(' || $this->a === ',' || $this->a === '=' ||
109
+ $this->a === ':' || $this->a === '[' || $this->a === '!' ||
110
+ $this->a === '&' || $this->a === '|' || $this->a === '?')) {
111
+
112
+ $this->output .= $this->a . $this->b;
113
+
114
+ for (;;) {
115
+ $this->a = $this->get();
116
+
117
+ if ($this->a === '/') {
118
+ break;
119
+ } elseif ($this->a === '\\') {
120
+ $this->output .= $this->a;
121
+ $this->a = $this->get();
122
+ } elseif (ord($this->a) <= self::ORD_LF) {
123
+ throw new JSMinException('Unterminated regular expression '.
124
+ 'literal.');
125
+ }
126
+
127
+ $this->output .= $this->a;
128
+ }
129
+
130
+ $this->b = $this->next();
131
+ }
132
+ }
133
+ }
134
+
135
+ protected function get() {
136
+ $c = $this->lookAhead;
137
+ $this->lookAhead = null;
138
+
139
+ if ($c === null) {
140
+ if ($this->inputIndex < $this->inputLength) {
141
+ $c = $this->input[$this->inputIndex];
142
+ $this->inputIndex += 1;
143
+ } else {
144
+ $c = null;
145
+ }
146
+ }
147
+
148
+ if ($c === "\r") {
149
+ return "\n";
150
+ }
151
+
152
+ if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
153
+ return $c;
154
+ }
155
+
156
+ return ' ';
157
+ }
158
+
159
+ protected function isAlphaNum($c) {
160
+ return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
161
+ }
162
+
163
+ protected function min() {
164
+ $this->a = "\n";
165
+ $this->action(3);
166
+
167
+ while ($this->a !== null) {
168
+ switch ($this->a) {
169
+ case ' ':
170
+ if ($this->isAlphaNum($this->b)) {
171
+ $this->action(1);
172
+ } else {
173
+ $this->action(2);
174
+ }
175
+ break;
176
+
177
+ case "\n":
178
+ switch ($this->b) {
179
+ case '{':
180
+ case '[':
181
+ case '(':
182
+ case '+':
183
+ case '-':
184
+ $this->action(1);
185
+ break;
186
+
187
+ case ' ':
188
+ $this->action(3);
189
+ break;
190
+
191
+ default:
192
+ if ($this->isAlphaNum($this->b)) {
193
+ $this->action(1);
194
+ }
195
+ else {
196
+ $this->action(2);
197
+ }
198
+ }
199
+ break;
200
+
201
+ default:
202
+ switch ($this->b) {
203
+ case ' ':
204
+ if ($this->isAlphaNum($this->a)) {
205
+ $this->action(1);
206
+ break;
207
+ }
208
+
209
+ $this->action(3);
210
+ break;
211
+
212
+ case "\n":
213
+ switch ($this->a) {
214
+ case '}':
215
+ case ']':
216
+ case ')':
217
+ case '+':
218
+ case '-':
219
+ case '"':
220
+ case "'":
221
+ $this->action(1);
222
+ break;
223
+
224
+ default:
225
+ if ($this->isAlphaNum($this->a)) {
226
+ $this->action(1);
227
+ }
228
+ else {
229
+ $this->action(3);
230
+ }
231
+ }
232
+ break;
233
+
234
+ default:
235
+ $this->action(1);
236
+ break;
237
+ }
238
+ }
239
+ }
240
+
241
+ return $this->output;
242
+ }
243
+
244
+ protected function next() {
245
+ $c = $this->get();
246
+
247
+ if ($c === '/') {
248
+ switch($this->peek()) {
249
+ case '/':
250
+ for (;;) {
251
+ $c = $this->get();
252
+
253
+ if (ord($c) <= self::ORD_LF) {
254
+ return $c;
255
+ }
256
+ }
257
+
258
+ case '*':
259
+ $this->get();
260
+
261
+ for (;;) {
262
+ switch($this->get()) {
263
+ case '*':
264
+ if ($this->peek() === '/') {
265
+ $this->get();
266
+ return ' ';
267
+ }
268
+ break;
269
+
270
+ case null:
271
+ throw new JSMinException('Unterminated comment.');
272
+ }
273
+ }
274
+
275
+ default:
276
+ return $c;
277
+ }
278
+ }
279
+
280
+ return $c;
281
+ }
282
+
283
+ protected function peek() {
284
+ $this->lookAhead = $this->get();
285
+ return $this->lookAhead;
286
+ }
287
+ }
288
+
289
+ // -- Exceptions ---------------------------------------------------------------
290
+ class JSMinException extends Exception {}
291
+ ?>
inc/minification/minify/minify-2.1.7-html.php ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class Minify_HTML
4
+ * @package Minify
5
+ */
6
+
7
+ /**
8
+ * Compress HTML
9
+ *
10
+ * This is a heavy regex-based removal of whitespace, unnecessary comments and
11
+ * tokens. IE conditional comments are preserved. There are also options to have
12
+ * STYLE and SCRIPT blocks compressed by callback functions.
13
+ *
14
+ * A test suite is available.
15
+ *
16
+ * @package Minify
17
+ * @author Stephen Clay <steve@mrclay.org>
18
+ */
19
+ class Minify_HTML {
20
+ /**
21
+ * @var boolean
22
+ */
23
+ protected $_jsCleanComments = true;
24
+
25
+ /**
26
+ * "Minify" an HTML page
27
+ *
28
+ * @param string $html
29
+ *
30
+ * @param array $options
31
+ *
32
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
33
+ * elements.
34
+ *
35
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
36
+ * elements. Note: the type attribute is ignored.
37
+ *
38
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
39
+ * unset, minify will sniff for an XHTML doctype.
40
+ *
41
+ * @return string
42
+ */
43
+ public static function minify($html, $options = array()) {
44
+ $min = new self($html, $options);
45
+ return $min->process();
46
+ }
47
+
48
+
49
+ /**
50
+ * Create a minifier object
51
+ *
52
+ * @param string $html
53
+ *
54
+ * @param array $options
55
+ *
56
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
57
+ * elements.
58
+ *
59
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
60
+ * elements. Note: the type attribute is ignored.
61
+ *
62
+ * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
63
+ *
64
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
65
+ * unset, minify will sniff for an XHTML doctype.
66
+ *
67
+ * @return null
68
+ */
69
+ public function __construct($html, $options = array())
70
+ {
71
+ $this->_html = str_replace("\r\n", "\n", trim($html));
72
+ if (isset($options['xhtml'])) {
73
+ $this->_isXhtml = (bool)$options['xhtml'];
74
+ }
75
+ if (isset($options['cssMinifier'])) {
76
+ $this->_cssMinifier = $options['cssMinifier'];
77
+ }
78
+ if (isset($options['jsMinifier'])) {
79
+ $this->_jsMinifier = $options['jsMinifier'];
80
+ }
81
+ if (isset($options['jsCleanComments'])) {
82
+ $this->_jsCleanComments = (bool)$options['jsCleanComments'];
83
+ }
84
+ }
85
+
86
+
87
+ /**
88
+ * Minify the markeup given in the constructor
89
+ *
90
+ * @return string
91
+ */
92
+ public function process()
93
+ {
94
+ if ($this->_isXhtml === null) {
95
+ $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
96
+ }
97
+
98
+ $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
99
+ $this->_placeholders = array();
100
+
101
+ // replace SCRIPTs (and minify) with placeholders
102
+ $this->_html = preg_replace_callback(
103
+ '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
104
+ ,array($this, '_removeScriptCB')
105
+ ,$this->_html);
106
+
107
+ // replace STYLEs (and minify) with placeholders
108
+ $this->_html = preg_replace_callback(
109
+ '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
110
+ ,array($this, '_removeStyleCB')
111
+ ,$this->_html);
112
+
113
+ // remove HTML comments (not containing IE conditional comments).
114
+ $this->_html = preg_replace_callback(
115
+ '/<!--([\\s\\S]*?)-->/'
116
+ ,array($this, '_commentCB')
117
+ ,$this->_html);
118
+
119
+ // replace PREs with placeholders
120
+ $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
121
+ ,array($this, '_removePreCB')
122
+ ,$this->_html);
123
+
124
+ // replace TEXTAREAs with placeholders
125
+ $this->_html = preg_replace_callback(
126
+ '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
127
+ ,array($this, '_removeTextareaCB')
128
+ ,$this->_html);
129
+
130
+ // trim each line.
131
+ // @todo take into account attribute values that span multiple lines.
132
+ $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
133
+
134
+ // remove ws around block/undisplayed elements
135
+ $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
136
+ .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
137
+ .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
138
+ .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
139
+ .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
140
+
141
+ // remove ws outside of all elements
142
+ $this->_html = preg_replace(
143
+ '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
144
+ ,'>$1$2$3<'
145
+ ,$this->_html);
146
+
147
+ // use newlines before 1st attribute in open tags (to limit line lengths)
148
+ $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
149
+
150
+ // fill placeholders
151
+ $this->_html = str_replace(
152
+ array_keys($this->_placeholders)
153
+ ,array_values($this->_placeholders)
154
+ ,$this->_html
155
+ );
156
+ // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
157
+ $this->_html = str_replace(
158
+ array_keys($this->_placeholders)
159
+ ,array_values($this->_placeholders)
160
+ ,$this->_html
161
+ );
162
+ return $this->_html;
163
+ }
164
+
165
+ protected function _commentCB($m)
166
+ {
167
+ return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
168
+ ? $m[0]
169
+ : '';
170
+ }
171
+
172
+ protected function _reservePlace($content)
173
+ {
174
+ $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
175
+ $this->_placeholders[$placeholder] = $content;
176
+ return $placeholder;
177
+ }
178
+
179
+ protected $_isXhtml = null;
180
+ protected $_replacementHash = null;
181
+ protected $_placeholders = array();
182
+ protected $_cssMinifier = null;
183
+ protected $_jsMinifier = null;
184
+
185
+ protected function _removePreCB($m)
186
+ {
187
+ return $this->_reservePlace("<pre{$m[1]}");
188
+ }
189
+
190
+ protected function _removeTextareaCB($m)
191
+ {
192
+ return $this->_reservePlace("<textarea{$m[1]}");
193
+ }
194
+
195
+ protected function _removeStyleCB($m)
196
+ {
197
+ $openStyle = "<style{$m[1]}";
198
+ $css = $m[2];
199
+ // remove HTML comments
200
+ $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
201
+
202
+ // remove CDATA section markers
203
+ $css = $this->_removeCdata($css);
204
+
205
+ // minify
206
+ $minifier = $this->_cssMinifier
207
+ ? $this->_cssMinifier
208
+ : 'trim';
209
+ $css = call_user_func($minifier, $css);
210
+
211
+ return $this->_reservePlace($this->_needsCdata($css)
212
+ ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
213
+ : "{$openStyle}{$css}</style>"
214
+ );
215
+ }
216
+
217
+ protected function _removeScriptCB($m)
218
+ {
219
+ $openScript = "<script{$m[2]}";
220
+ $js = $m[3];
221
+
222
+ // whitespace surrounding? preserve at least one space
223
+ $ws1 = ($m[1] === '') ? '' : ' ';
224
+ $ws2 = ($m[4] === '') ? '' : ' ';
225
+
226
+ // remove HTML comments (and ending "//" if present)
227
+ if ($this->_jsCleanComments) {
228
+ $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
229
+ }
230
+
231
+ // remove CDATA section markers
232
+ $js = $this->_removeCdata($js);
233
+
234
+ // minify
235
+ $minifier = $this->_jsMinifier
236
+ ? $this->_jsMinifier
237
+ : 'trim';
238
+ $js = call_user_func($minifier, $js);
239
+
240
+ return $this->_reservePlace($this->_needsCdata($js)
241
+ ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
242
+ : "{$ws1}{$openScript}{$js}</script>{$ws2}"
243
+ );
244
+ }
245
+
246
+ protected function _removeCdata($str)
247
+ {
248
+ return (false !== strpos($str, '<![CDATA['))
249
+ ? str_replace(array('<![CDATA[', ']]>'), '', $str)
250
+ : $str;
251
+ }
252
+
253
+ protected function _needsCdata($str)
254
+ {
255
+ return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
256
+ }
257
+ }
inc/minification/minify/minify-2.1.7-jsmin.php ADDED
@@ -0,0 +1,447 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
4
+ *
5
+ * <code>
6
+ * $minifiedJs = JSMin::minify($js);
7
+ * </code>
8
+ *
9
+ * This is a modified port of jsmin.c. Improvements:
10
+ *
11
+ * Does not choke on some regexp literals containing quote characters. E.g. /'/
12
+ *
13
+ * Spaces are preserved after some add/sub operators, so they are not mistakenly
14
+ * converted to post-inc/dec. E.g. a + ++b -> a+ ++b
15
+ *
16
+ * Preserves multi-line comments that begin with /*!
17
+ *
18
+ * PHP 5 or higher is required.
19
+ *
20
+ * Permission is hereby granted to use this version of the library under the
21
+ * same terms as jsmin.c, which has the following license:
22
+ *
23
+ * --
24
+ * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
25
+ *
26
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
27
+ * this software and associated documentation files (the "Software"), to deal in
28
+ * the Software without restriction, including without limitation the rights to
29
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
30
+ * of the Software, and to permit persons to whom the Software is furnished to do
31
+ * so, subject to the following conditions:
32
+ *
33
+ * The above copyright notice and this permission notice shall be included in all
34
+ * copies or substantial portions of the Software.
35
+ *
36
+ * The Software shall be used for Good, not Evil.
37
+ *
38
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44
+ * SOFTWARE.
45
+ * --
46
+ *
47
+ * @package JSMin
48
+ * @author Ryan Grove <ryan@wonko.com> (PHP port)
49
+ * @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
50
+ * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
51
+ * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
52
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
53
+ * @license http://opensource.org/licenses/mit-license.php MIT License
54
+ * @link http://code.google.com/p/jsmin-php/
55
+ */
56
+
57
+ class JSMin {
58
+ const ORD_LF = 10;
59
+ const ORD_SPACE = 32;
60
+ const ACTION_KEEP_A = 1;
61
+ const ACTION_DELETE_A = 2;
62
+ const ACTION_DELETE_A_B = 3;
63
+
64
+ protected $a = "\n";
65
+ protected $b = '';
66
+ protected $input = '';
67
+ protected $inputIndex = 0;
68
+ protected $inputLength = 0;
69
+ protected $lookAhead = null;
70
+ protected $output = '';
71
+ protected $lastByteOut = '';
72
+ protected $keptComment = '';
73
+
74
+ /**
75
+ * Minify Javascript.
76
+ *
77
+ * @param string $js Javascript to be minified
78
+ *
79
+ * @return string
80
+ */
81
+ public static function minify($js)
82
+ {
83
+ $jsmin = new JSMin($js);
84
+ return $jsmin->min();
85
+ }
86
+
87
+ /**
88
+ * @param string $input
89
+ */
90
+ public function __construct($input)
91
+ {
92
+ $this->input = $input;
93
+ }
94
+
95
+ /**
96
+ * Perform minification, return result
97
+ *
98
+ * @return string
99
+ */
100
+ public function min()
101
+ {
102
+ if ($this->output !== '') { // min already run
103
+ return $this->output;
104
+ }
105
+
106
+ $mbIntEnc = null;
107
+ if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
108
+ $mbIntEnc = mb_internal_encoding();
109
+ mb_internal_encoding('8bit');
110
+ }
111
+ $this->input = str_replace("\r\n", "\n", $this->input);
112
+ $this->inputLength = strlen($this->input);
113
+
114
+ $this->action(self::ACTION_DELETE_A_B);
115
+
116
+ while ($this->a !== null) {
117
+ // determine next command
118
+ $command = self::ACTION_KEEP_A; // default
119
+ if ($this->a === ' ') {
120
+ if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
121
+ && ($this->b === $this->lastByteOut)) {
122
+ // Don't delete this space. If we do, the addition/subtraction
123
+ // could be parsed as a post-increment
124
+ } elseif (! $this->isAlphaNum($this->b)) {
125
+ $command = self::ACTION_DELETE_A;
126
+ }
127
+ } elseif ($this->a === "\n") {
128
+ if ($this->b === ' ') {
129
+ $command = self::ACTION_DELETE_A_B;
130
+
131
+ // in case of mbstring.func_overload & 2, must check for null b,
132
+ // otherwise mb_strpos will give WARNING
133
+ } elseif ($this->b === null
134
+ || (false === strpos('{[(+-!~', $this->b)
135
+ && ! $this->isAlphaNum($this->b))) {
136
+ $command = self::ACTION_DELETE_A;
137
+ }
138
+ } elseif (! $this->isAlphaNum($this->a)) {
139
+ if ($this->b === ' '
140
+ || ($this->b === "\n"
141
+ && (false === strpos('}])+-"\'', $this->a)))) {
142
+ $command = self::ACTION_DELETE_A_B;
143
+ }
144
+ }
145
+ $this->action($command);
146
+ }
147
+ $this->output = trim($this->output);
148
+
149
+ if ($mbIntEnc !== null) {
150
+ mb_internal_encoding($mbIntEnc);
151
+ }
152
+ return $this->output;
153
+ }
154
+
155
+ /**
156
+ * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
157
+ * ACTION_DELETE_A = Copy B to A. Get the next B.
158
+ * ACTION_DELETE_A_B = Get the next B.
159
+ *
160
+ * @param int $command
161
+ * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
162
+ */
163
+ protected function action($command)
164
+ {
165
+ // make sure we don't compress "a + ++b" to "a+++b", etc.
166
+ if ($command === self::ACTION_DELETE_A_B
167
+ && $this->b === ' '
168
+ && ($this->a === '+' || $this->a === '-')) {
169
+ // Note: we're at an addition/substraction operator; the inputIndex
170
+ // will certainly be a valid index
171
+ if ($this->input[$this->inputIndex] === $this->a) {
172
+ // This is "+ +" or "- -". Don't delete the space.
173
+ $command = self::ACTION_KEEP_A;
174
+ }
175
+ }
176
+
177
+ switch ($command) {
178
+ case self::ACTION_KEEP_A: // 1
179
+ $this->output .= $this->a;
180
+
181
+ if ($this->keptComment) {
182
+ $this->output = rtrim($this->output, "\n");
183
+ $this->output .= $this->keptComment;
184
+ $this->keptComment = '';
185
+ }
186
+
187
+ $this->lastByteOut = $this->a;
188
+
189
+ // fallthrough intentional
190
+ case self::ACTION_DELETE_A: // 2
191
+ $this->a = $this->b;
192
+ if ($this->a === "'" || $this->a === '"') { // string literal
193
+ $str = $this->a; // in case needed for exception
194
+ for(;;) {
195
+ $this->output .= $this->a;
196
+ $this->lastByteOut = $this->a;
197
+
198
+ $this->a = $this->get();
199
+ if ($this->a === $this->b) { // end quote
200
+ break;
201
+ }
202
+ if ($this->isEOF($this->a)) {
203
+ throw new JSMin_UnterminatedStringException(
204
+ "JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
205
+ }
206
+ $str .= $this->a;
207
+ if ($this->a === '\\') {
208
+ $this->output .= $this->a;
209
+ $this->lastByteOut = $this->a;
210
+
211
+ $this->a = $this->get();
212
+ $str .= $this->a;
213
+ }
214
+ }
215
+ }
216
+
217
+ // fallthrough intentional
218
+ case self::ACTION_DELETE_A_B: // 3
219
+ $this->b = $this->next();
220
+ if ($this->b === '/' && $this->isRegexpLiteral()) {
221
+ $this->output .= $this->a . $this->b;
222
+ $pattern = '/'; // keep entire pattern in case we need to report it in the exception
223
+ for(;;) {
224
+ $this->a = $this->get();
225
+ $pattern .= $this->a;
226
+ if ($this->a === '[') {
227
+ for(;;) {
228
+ $this->output .= $this->a;
229
+ $this->a = $this->get();
230
+ $pattern .= $this->a;
231
+ if ($this->a === ']') {
232
+ break;
233
+ }
234
+ if ($this->a === '\\') {
235
+ $this->output .= $this->a;
236
+ $this->a = $this->get();
237
+ $pattern .= $this->a;
238
+ }
239
+ if ($this->isEOF($this->a)) {
240
+ throw new JSMin_UnterminatedRegExpException(
241
+ "JSMin: Unterminated set in RegExp at byte "
242
+ . $this->inputIndex .": {$pattern}");
243
+ }
244
+ }
245
+ }
246
+
247
+ if ($this->a === '/') { // end pattern
248
+ break; // while (true)
249
+ } elseif ($this->a === '\\') {
250
+ $this->output .= $this->a;
251
+ $this->a = $this->get();
252
+ $pattern .= $this->a;
253
+ } elseif ($this->isEOF($this->a)) {
254
+ throw new JSMin_UnterminatedRegExpException(
255
+ "JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
256
+ }
257
+ $this->output .= $this->a;
258
+ $this->lastByteOut = $this->a;
259
+ }
260
+ $this->b = $this->next();
261
+ }
262
+ // end case ACTION_DELETE_A_B
263
+ }
264
+ }
265
+
266
+ /**
267
+ * @return bool
268
+ */
269
+ protected function isRegexpLiteral()
270
+ {
271
+ if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
272
+ // we obviously aren't dividing
273
+ return true;
274
+ }
275
+
276
+ // we have to check for a preceding keyword, and we don't need to pattern
277
+ // match over the whole output.
278
+ $recentOutput = substr($this->output, -10);
279
+
280
+ // check if return/typeof directly precede a pattern without a space
281
+ foreach (array('return', 'typeof') as $keyword) {
282
+ if ($this->a !== substr($keyword, -1)) {
283
+ // certainly wasn't keyword
284
+ continue;
285
+ }
286
+ if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
287
+ if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
288
+ return true;
289
+ }
290
+ }
291
+ }
292
+
293
+ // check all keywords
294
+ if ($this->a === ' ' || $this->a === "\n") {
295
+ if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
296
+ if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
297
+ return true;
298
+ }
299
+ }
300
+ }
301
+
302
+ return false;
303
+ }
304
+
305
+ /**
306
+ * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
307
+ * translate it to a space or linefeed.
308
+ *
309
+ * @return string
310
+ */
311
+ protected function get()
312
+ {
313
+ $c = $this->lookAhead;
314
+ $this->lookAhead = null;
315
+ if ($c === null) {
316
+ // getc(stdin)
317
+ if ($this->inputIndex < $this->inputLength) {
318
+ $c = $this->input[$this->inputIndex];
319
+ $this->inputIndex += 1;
320
+ } else {
321
+ $c = null;
322
+ }
323
+ }
324
+ if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
325
+ return $c;
326
+ }
327
+ if ($c === "\r") {
328
+ return "\n";
329
+ }
330
+ return ' ';
331
+ }
332
+
333
+ /**
334
+ * Does $a indicate end of input?
335
+ *
336
+ * @param string $a
337
+ * @return bool
338
+ */
339
+ protected function isEOF($a)
340
+ {
341
+ return ord($a) <= self::ORD_LF;
342
+ }
343
+
344
+ /**
345
+ * Get next char (without getting it). If is ctrl character, translate to a space or newline.
346
+ *
347
+ * @return string
348
+ */
349
+ protected function peek()
350
+ {
351
+ $this->lookAhead = $this->get();
352
+ return $this->lookAhead;
353
+ }
354
+
355
+ /**
356
+ * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
357
+ *
358
+ * @param string $c
359
+ *
360
+ * @return bool
361
+ */
362
+ protected function isAlphaNum($c)
363
+ {
364
+ return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
365
+ }
366
+
367
+ /**
368
+ * Consume a single line comment from input (possibly retaining it)
369
+ */
370
+ protected function consumeSingleLineComment()
371
+ {
372
+ $comment = '';
373
+ while (true) {
374
+ $get = $this->get();
375
+ $comment .= $get;
376
+ if (ord($get) <= self::ORD_LF) { // end of line reached
377
+ // if IE conditional comment
378
+ if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
379
+ $this->keptComment .= "/{$comment}";
380
+ }
381
+ return;
382
+ }
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Consume a multiple line comment from input (possibly retaining it)
388
+ *
389
+ * @throws JSMin_UnterminatedCommentException
390
+ */
391
+ protected function consumeMultipleLineComment()
392
+ {
393
+ $this->get();
394
+ $comment = '';
395
+ for(;;) {
396
+ $get = $this->get();
397
+ if ($get === '*') {
398
+ if ($this->peek() === '/') { // end of comment reached
399
+ $this->get();
400
+ if (0 === strpos($comment, '!')) {
401
+ // preserved by YUI Compressor
402
+ if (!$this->keptComment) {
403
+ // don't prepend a newline if two comments right after one another
404
+ $this->keptComment = "\n";
405
+ }
406
+ $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
407
+ } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
408
+ // IE conditional
409
+ $this->keptComment .= "/*{$comment}*/";
410
+ }
411
+ return;
412
+ }
413
+ } elseif ($get === null) {
414
+ throw new JSMin_UnterminatedCommentException(
415
+ "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
416
+ }
417
+ $comment .= $get;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Get the next character, skipping over comments. Some comments may be preserved.
423
+ *
424
+ * @return string
425
+ */
426
+ protected function next()
427
+ {
428
+ $get = $this->get();
429
+ if ($get === '/') {
430
+ switch ($this->peek()) {
431
+ case '/':
432
+ $this->consumeSingleLineComment();
433
+ $get = "\n";
434
+ break;
435
+ case '*':
436
+ $this->consumeMultipleLineComment();
437
+ $get = ' ';
438
+ break;
439
+ }
440
+ }
441
+ return $get;
442
+ }
443
+ }
444
+
445
+ class JSMin_UnterminatedStringException extends Exception {}
446
+ class JSMin_UnterminatedCommentException extends Exception {}
447
+ class JSMin_UnterminatedRegExpException extends Exception {}
inc/minification/minify/minify-css-compressor.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class Minify_CSS_Compressor
4
+ * @package Minify
5
+ */
6
+
7
+ /**
8
+ * Compress CSS
9
+ *
10
+ * This is a heavy regex-based removal of whitespace, unnecessary
11
+ * comments and tokens, and some CSS value minimization, where practical.
12
+ * Many steps have been taken to avoid breaking comment-based hacks,
13
+ * including the ie5/mac filter (and its inversion), but expect tricky
14
+ * hacks involving comment tokens in 'content' value strings to break
15
+ * minimization badly. A test suite is available.
16
+ *
17
+ * @package Minify
18
+ * @author Stephen Clay <steve@mrclay.org>
19
+ * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
20
+ */
21
+ class Minify_CSS_Compressor {
22
+
23
+ /**
24
+ * Minify a CSS string
25
+ *
26
+ * @param string $css
27
+ *
28
+ * @param array $options (currently ignored)
29
+ *
30
+ * @return string
31
+ */
32
+ public static function process($css, $options = array())
33
+ {
34
+ $obj = new Minify_CSS_Compressor($options);
35
+ return $obj->_process($css);
36
+ }
37
+
38
+ /**
39
+ * @var array options
40
+ */
41
+ protected $_options = null;
42
+
43
+ /**
44
+ * @var bool Are we "in" a hack?
45
+ *
46
+ * I.e. are some browsers targetted until the next comment?
47
+ */
48
+ protected $_inHack = false;
49
+
50
+
51
+ /**
52
+ * Constructor
53
+ *
54
+ * @param array $options (currently ignored)
55
+ *
56
+ * @return null
57
+ */
58
+ private function __construct($options) {
59
+ $this->_options = $options;
60
+ }
61
+
62
+ /**
63
+ * Minify a CSS string
64
+ *
65
+ * @param string $css
66
+ *
67
+ * @return string
68
+ */
69
+ protected function _process($css)
70
+ {
71
+ $css = str_replace("\r\n", "\n", $css);
72
+
73
+ // preserve empty comment after '>'
74
+ // http://www.webdevout.net/css-hacks#in_css-selectors
75
+ $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
76
+
77
+ // preserve empty comment between property and value
78
+ // http://css-discuss.incutio.com/?page=BoxModelHack
79
+ $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
80
+ $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
81
+
82
+ // apply callback to all valid comments (and strip out surrounding ws
83
+ $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
84
+ ,array($this, '_commentCB'), $css);
85
+
86
+ // remove ws around { } and last semicolon in declaration block
87
+ $css = preg_replace('/\\s*{\\s*/', '{', $css);
88
+ $css = preg_replace('/;?\\s*}\\s*/', '}', $css);
89
+
90
+ // remove ws surrounding semicolons
91
+ $css = preg_replace('/\\s*;\\s*/', ';', $css);
92
+
93
+ // remove ws around urls
94
+ $css = preg_replace('/
95
+ url\\( # url(
96
+ \\s*
97
+ ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
98
+ \\s*
99
+ \\) # )
100
+ /x', 'url($1)', $css);
101
+
102
+ // remove ws between rules and colons
103
+ $css = preg_replace('/
104
+ \\s*
105
+ ([{;]) # 1 = beginning of block or rule separator
106
+ \\s*
107
+ ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
108
+ \\s*
109
+ :
110
+ \\s*
111
+ (\\b|[#\'"]) # 3 = first character of a value
112
+ /x', '$1$2:$3', $css);
113
+
114
+ // remove ws in selectors
115
+ $css = preg_replace_callback('/
116
+ (?: # non-capture
117
+ \\s*
118
+ [^~>+,\\s]+ # selector part
119
+ \\s*
120
+ [,>+~] # combinators
121
+ )+
122
+ \\s*
123
+ [^~>+,\\s]+ # selector part
124
+ { # open declaration block
125
+ /x'
126
+ ,array($this, '_selectorsCB'), $css);
127
+
128
+ // minimize hex colors
129
+ $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
130
+ , '$1#$2$3$4$5', $css);
131
+
132
+ // remove spaces between font families
133
+ $css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
134
+ ,array($this, '_fontFamilyCB'), $css);
135
+
136
+ $css = preg_replace('/@import\\s+url/', '@import url', $css);
137
+
138
+ // replace any ws involving newlines with a single newline
139
+ $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
140
+
141
+ // separate common descendent selectors w/ newlines (to limit line lengths)
142
+ $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
143
+
144
+ // Use newline after 1st numeric value (to limit line lengths).
145
+ $css = preg_replace('/
146
+ ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
147
+ \\s+
148
+ /x'
149
+ ,"$1\n", $css);
150
+
151
+ // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
152
+ $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
153
+
154
+ return trim($css);
155
+ }
156
+
157
+ /**
158
+ * Replace what looks like a set of selectors
159
+ *
160
+ * @param array $m regex matches
161
+ *
162
+ * @return string
163
+ */
164
+ protected function _selectorsCB($m)
165
+ {
166
+ // remove ws around the combinators
167
+ return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
168
+ }
169
+
170
+ /**
171
+ * Process a comment and return a replacement
172
+ *
173
+ * @param array $m regex matches
174
+ *
175
+ * @return string
176
+ */
177
+ protected function _commentCB($m)
178
+ {
179
+ $hasSurroundingWs = (trim($m[0]) !== $m[1]);
180
+ $m = $m[1];
181
+ // $m is the comment content w/o the surrounding tokens,
182
+ // but the return value will replace the entire comment.
183
+ if ($m === 'keep') {
184
+ return '/**/';
185
+ }
186
+ if ($m === '" "') {
187
+ // component of http://tantek.com/CSS/Examples/midpass.html
188
+ return '/*" "*/';
189
+ }
190
+ if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
191
+ // component of http://tantek.com/CSS/Examples/midpass.html
192
+ return '/*";}}/* */';
193
+ }
194
+ if ($this->_inHack) {
195
+ // inversion: feeding only to one browser
196
+ if (preg_match('@
197
+ ^/ # comment started like /*/
198
+ \\s*
199
+ (\\S[\\s\\S]+?) # has at least some non-ws content
200
+ \\s*
201
+ /\\* # ends like /*/ or /**/
202
+ @x', $m, $n)) {
203
+ // end hack mode after this comment, but preserve the hack and comment content
204
+ $this->_inHack = false;
205
+ return "/*/{$n[1]}/**/";
206
+ }
207
+ }
208
+ if (substr($m, -1) === '\\') { // comment ends like \*/
209
+ // begin hack mode and preserve hack
210
+ $this->_inHack = true;
211
+ return '/*\\*/';
212
+ }
213
+ if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
214
+ // begin hack mode and preserve hack
215
+ $this->_inHack = true;
216
+ return '/*/*/';
217
+ }
218
+ if ($this->_inHack) {
219
+ // a regular comment ends hack mode but should be preserved
220
+ $this->_inHack = false;
221
+ return '/**/';
222
+ }
223
+ // Issue 107: if there's any surrounding whitespace, it may be important, so
224
+ // replace the comment with a single space
225
+ return $hasSurroundingWs // remove all other comments
226
+ ? ' '
227
+ : '';
228
+ }
229
+
230
+ /**
231
+ * Process a font-family listing and return a replacement
232
+ *
233
+ * @param array $m regex matches
234
+ *
235
+ * @return string
236
+ */
237
+ protected function _fontFamilyCB($m)
238
+ {
239
+ $m[1] = preg_replace('/
240
+ \\s*
241
+ (
242
+ "[^"]+" # 1 = family in double qutoes
243
+ |\'[^\']+\' # or 1 = family in single quotes
244
+ |[\\w\\-]+ # or 1 = unquoted family
245
+ )
246
+ \\s*
247
+ /x', '$1', $m[1]);
248
+ return 'font-family:' . $m[1] . $m[2];
249
+ }
250
+ }
inc/minification/minify/minify-html.php ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class Minify_HTML
4
+ * @package Minify
5
+ */
6
+
7
+ /**
8
+ * Compress HTML
9
+ *
10
+ * This is a heavy regex-based removal of whitespace, unnecessary comments and
11
+ * tokens. IE conditional comments are preserved. There are also options to have
12
+ * STYLE and SCRIPT blocks compressed by callback functions.
13
+ *
14
+ * A test suite is available.
15
+ *
16
+ * @package Minify
17
+ * @author Stephen Clay <steve@mrclay.org>
18
+ */
19
+ class Minify_HTML {
20
+
21
+ /**
22
+ * "Minify" an HTML page
23
+ *
24
+ * @param string $html
25
+ *
26
+ * @param array $options
27
+ *
28
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
29
+ * elements.
30
+ *
31
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
32
+ * elements. Note: the type attribute is ignored.
33
+ *
34
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
35
+ * unset, minify will sniff for an XHTML doctype.
36
+ *
37
+ * 'keepComments' : (optional boolean) should the HTML comments be kept
38
+ * in the HTML Code?
39
+ *
40
+ * @return string
41
+ */
42
+ public static function minify($html, $options = array()) {
43
+ $min = new Minify_HTML($html, $options);
44
+ return $min->process();
45
+ }
46
+
47
+
48
+ /**
49
+ * Create a minifier object
50
+ *
51
+ * @param string $html
52
+ *
53
+ * @param array $options
54
+ *
55
+ * 'cssMinifier' : (optional) callback function to process content of STYLE
56
+ * elements.
57
+ *
58
+ * 'jsMinifier' : (optional) callback function to process content of SCRIPT
59
+ * elements. Note: the type attribute is ignored.
60
+ *
61
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
62
+ * unset, minify will sniff for an XHTML doctype.
63
+ *
64
+ * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
65
+ * unset, minify will sniff for an XHTML doctype.
66
+ *
67
+ * @return null
68
+ */
69
+ public function __construct($html, $options = array())
70
+ {
71
+ $this->_html = str_replace("\r\n", "\n", trim($html));
72
+ if (isset($options['xhtml'])) {
73
+ $this->_isXhtml = (bool)$options['xhtml'];
74
+ }
75
+ if (isset($options['cssMinifier'])) {
76
+ $this->_cssMinifier = $options['cssMinifier'];
77
+ }
78
+ if (isset($options['jsMinifier'])) {
79
+ $this->_jsMinifier = $options['jsMinifier'];
80
+ }
81
+ if (isset($options['keepComments'])) {
82
+ $this->_keepComments = $options['keepComments'];
83
+ }
84
+ }
85
+
86
+
87
+ /**
88
+ * Minify the markeup given in the constructor
89
+ *
90
+ * @return string
91
+ */
92
+ public function process()
93
+ {
94
+ if ($this->_isXhtml === null) {
95
+ $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
96
+ }
97
+
98
+ $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
99
+ $this->_placeholders = array();
100
+
101
+ // replace SCRIPTs (and minify) with placeholders
102
+ $this->_html = preg_replace_callback(
103
+ '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
104
+ ,array($this, '_removeScriptCB')
105
+ ,$this->_html);
106
+
107
+ // replace STYLEs (and minify) with placeholders
108
+ $this->_html = preg_replace_callback(
109
+ '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
110
+ ,array($this, '_removeStyleCB')
111
+ ,$this->_html);
112
+
113
+ // remove HTML comments (not containing IE conditional comments).
114
+ if ($this->_keepComments == false) {
115
+ $this->_html = preg_replace_callback(
116
+ '/<!--([\\s\\S]*?)-->/'
117
+ ,array($this, '_commentCB')
118
+ ,$this->_html);
119
+ }
120
+
121
+ // replace PREs with placeholders
122
+ $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
123
+ ,array($this, '_removePreCB')
124
+ ,$this->_html);
125
+
126
+ // replace TEXTAREAs with placeholders
127
+ $this->_html = preg_replace_callback(
128
+ '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
129
+ ,array($this, '_removeTextareaCB')
130
+ ,$this->_html);
131
+
132
+ // replace data: URIs with placeholders
133
+ $this->_html = preg_replace_callback(
134
+ '/(=("|\')data:.*\\2)/Ui'
135
+ ,array($this, '_removeDataURICB')
136
+ ,$this->_html);
137
+
138
+ // trim each line.
139
+ // @todo take into account attribute values that span multiple lines.
140
+ $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
141
+
142
+ // remove ws around block/undisplayed elements
143
+ $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
144
+ .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
145
+ .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
146
+ .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
147
+ .'|ul|video)\\b[^>]*>)/i', '$1', $this->_html);
148
+
149
+ // remove ws outside of all elements
150
+ $this->_html = preg_replace_callback(
151
+ '/>([^<]+)</'
152
+ ,array($this, '_outsideTagCB')
153
+ ,$this->_html);
154
+
155
+ // use newlines before 1st attribute in open tags (to limit line lengths)
156
+ //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
157
+
158
+ // fill placeholders
159
+ $this->_html = str_replace(
160
+ array_keys($this->_placeholders)
161
+ ,array_values($this->_placeholders)
162
+ ,$this->_html
163
+ );
164
+ return $this->_html;
165
+ }
166
+
167
+ protected function _commentCB($m)
168
+ {
169
+ return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
170
+ ? $m[0]
171
+ : '';
172
+ }
173
+
174
+ protected function _reservePlace($content)
175
+ {
176
+ $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
177
+ $this->_placeholders[$placeholder] = $content;
178
+ return $placeholder;
179
+ }
180
+
181
+ protected $_isXhtml = null;
182
+ protected $_replacementHash = null;
183
+ protected $_placeholders = array();
184
+ protected $_cssMinifier = null;
185
+ protected $_jsMinifier = null;
186
+ protected $_keepComments = false;
187
+
188
+ protected function _outsideTagCB($m)
189
+ {
190
+ return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
191
+ }
192
+
193
+ protected function _removePreCB($m)
194
+ {
195
+ return $this->_reservePlace($m[1]);
196
+ }
197
+
198
+ protected function _removeTextareaCB($m)
199
+ {
200
+ return $this->_reservePlace($m[1]);
201
+ }
202
+
203
+ protected function _removeDataURICB($m)
204
+ {
205
+ return $this->_reservePlace($m[1]);
206
+ }
207
+
208
+ protected function _removeStyleCB($m)
209
+ {
210
+ $openStyle = $m[1];
211
+ $css = $m[2];
212
+ // remove HTML comments
213
+ $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
214
+
215
+ // remove CDATA section markers
216
+ $css = $this->_removeCdata($css);
217
+
218
+ // minify
219
+ $minifier = $this->_cssMinifier
220
+ ? $this->_cssMinifier
221
+ : 'trim';
222
+ $css = call_user_func($minifier, $css);
223
+
224
+ return $this->_reservePlace($this->_needsCdata($css)
225
+ ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
226
+ : "{$openStyle}{$css}</style>"
227
+ );
228
+ }
229
+
230
+ protected function _removeScriptCB($m)
231
+ {
232
+ $openScript = $m[2];
233
+ $js = $m[3];
234
+
235
+ // whitespace surrounding? preserve at least one space
236
+ $ws1 = ($m[1] === '') ? '' : ' ';
237
+ $ws2 = ($m[4] === '') ? '' : ' ';
238
+
239
+ // remove HTML comments (and ending "//" if present)
240
+ $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
241
+
242
+ // remove CDATA section markers
243
+ $js = $this->_removeCdata($js);
244
+
245
+ // minify
246
+ $minifier = $this->_jsMinifier
247
+ ? $this->_jsMinifier
248
+ : 'trim';
249
+ $js = call_user_func($minifier, $js);
250
+
251
+ return $this->_reservePlace($this->_needsCdata($js)
252
+ ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
253
+ : "{$ws1}{$openScript}{$js}</script>{$ws2}"
254
+ );
255
+ }
256
+
257
+ protected function _removeCdata($str)
258
+ {
259
+ return (false !== strpos($str, '<![CDATA['))
260
+ ? str_replace(array('/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str)
261
+ : $str;
262
+ }
263
+
264
+ protected function _needsCdata($str)
265
+ {
266
+ return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
267
+ }
268
+ }
inc/minification/minify/yui-php-cssmin-2.4.8-4.php ADDED
@@ -0,0 +1,777 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*!
4
+ * cssmin.php v2.4.8-4
5
+ * Author: Tubal Martin - http://tubalmartin.me/
6
+ * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
7
+ *
8
+ * This is a PHP port of the CSS minification tool distributed with YUICompressor,
9
+ * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
10
+ * Permission is hereby granted to use the PHP version under the same
11
+ * conditions as the YUICompressor.
12
+ */
13
+
14
+ /*!
15
+ * YUI Compressor
16
+ * http://developer.yahoo.com/yui/compressor/
17
+ * Author: Julien Lecomte - http://www.julienlecomte.net/
18
+ * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
19
+ * The copyrights embodied in the content of this file are licensed
20
+ * by Yahoo! Inc. under the BSD (revised) open source license.
21
+ */
22
+
23
+ class CSSmin
24
+ {
25
+ const NL = '___YUICSSMIN_PRESERVED_NL___';
26
+ const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
27
+ const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
28
+ const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
29
+ const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
30
+
31
+ private $comments;
32
+ private $preserved_tokens;
33
+ private $memory_limit;
34
+ private $max_execution_time;
35
+ private $pcre_backtrack_limit;
36
+ private $pcre_recursion_limit;
37
+ private $raise_php_limits;
38
+
39
+ /**
40
+ * @param bool|int $raise_php_limits
41
+ * If true, PHP settings will be raised if needed
42
+ */
43
+ public function __construct($raise_php_limits = TRUE)
44
+ {
45
+ // Set suggested PHP limits
46
+ $this->memory_limit = 128 * 1048576; // 128MB in bytes
47
+ $this->max_execution_time = 60; // 1 min
48
+ $this->pcre_backtrack_limit = 1000 * 1000;
49
+ $this->pcre_recursion_limit = 500 * 1000;
50
+
51
+ $this->raise_php_limits = (bool) $raise_php_limits;
52
+ }
53
+
54
+ /**
55
+ * Minify a string of CSS
56
+ * @param string $css
57
+ * @param int|bool $linebreak_pos
58
+ * @return string
59
+ */
60
+ public function run($css = '', $linebreak_pos = FALSE)
61
+ {
62
+ if (empty($css)) {
63
+ return '';
64
+ }
65
+
66
+ if ($this->raise_php_limits) {
67
+ $this->do_raise_php_limits();
68
+ }
69
+
70
+ $this->comments = array();
71
+ $this->preserved_tokens = array();
72
+
73
+ $start_index = 0;
74
+ $length = strlen($css);
75
+
76
+ $css = $this->extract_data_urls($css);
77
+
78
+ // collect all comment blocks...
79
+ while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
80
+ $end_index = $this->index_of($css, '*/', $start_index + 2);
81
+ if ($end_index < 0) {
82
+ $end_index = $length;
83
+ }
84
+ $comment_found = $this->str_slice($css, $start_index + 2, $end_index);
85
+ $this->comments[] = $comment_found;
86
+ $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
87
+ $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
88
+ // Set correct start_index: Fixes issue #2528130
89
+ $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
90
+ }
91
+
92
+ // preserve strings so their content doesn't get accidentally minified
93
+ $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
94
+
95
+ // Let's divide css code in chunks of 5.000 chars aprox.
96
+ // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
97
+ // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
98
+ // long strings and a (sub)pattern matches a number of chars greater than
99
+ // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
100
+ // returning NULL and $css would be empty.
101
+ $charset = '';
102
+ $charset_regexp = '/(@charset)( [^;]+;)/i';
103
+ $css_chunks = array();
104
+ $css_chunk_length = 5000; // aprox size, not exact
105
+ $start_index = 0;
106
+ $i = $css_chunk_length; // save initial iterations
107
+ $l = strlen($css);
108
+
109
+
110
+ // if the number of characters is 5000 or less, do not chunk
111
+ if ($l <= $css_chunk_length) {
112
+ $css_chunks[] = $css;
113
+ } else {
114
+ // chunk css code securely
115
+ while ($i < $l) {
116
+ $i += 50; // save iterations
117
+ if ($l - $start_index <= $css_chunk_length || $i >= $l) {
118
+ $css_chunks[] = $this->str_slice($css, $start_index);
119
+ break;
120
+ }
121
+ if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
122
+ // If there are two ending curly braces }} separated or not by spaces,
123
+ // join them in the same chunk (i.e. @media blocks)
124
+ $next_chunk = substr($css, $i);
125
+ if (preg_match('/^\s*\}/', $next_chunk)) {
126
+ $i = $i + $this->index_of($next_chunk, '}') + 1;
127
+ }
128
+
129
+ $css_chunks[] = $this->str_slice($css, $start_index, $i);
130
+ $start_index = $i;
131
+ }
132
+ }
133
+ }
134
+
135
+ // Minify each chunk
136
+ for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
137
+ $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
138
+ // Keep the first @charset at-rule found
139
+ if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
140
+ $charset = strtolower($matches[1]) . $matches[2];
141
+ }
142
+ // Delete all @charset at-rules
143
+ $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
144
+ }
145
+
146
+ // Update the first chunk and push the charset to the top of the file.
147
+ $css_chunks[0] = $charset . $css_chunks[0];
148
+
149
+ return implode('', $css_chunks);
150
+ }
151
+
152
+ /**
153
+ * Sets the memory limit for this script
154
+ * @param int|string $limit
155
+ */
156
+ public function set_memory_limit($limit)
157
+ {
158
+ $this->memory_limit = $this->normalize_int($limit);
159
+ }
160
+
161
+ /**
162
+ * Sets the maximum execution time for this script
163
+ * @param int|string $seconds
164
+ */
165
+ public function set_max_execution_time($seconds)
166
+ {
167
+ $this->max_execution_time = (int) $seconds;
168
+ }
169
+
170
+ /**
171
+ * Sets the PCRE backtrack limit for this script
172
+ * @param int $limit
173
+ */
174
+ public function set_pcre_backtrack_limit($limit)
175
+ {
176
+ $this->pcre_backtrack_limit = (int) $limit;
177
+ }
178
+
179
+ /**
180
+ * Sets the PCRE recursion limit for this script
181
+ * @param int $limit
182
+ */
183
+ public function set_pcre_recursion_limit($limit)
184
+ {
185
+ $this->pcre_recursion_limit = (int) $limit;
186
+ }
187
+
188
+ /**
189
+ * Try to configure PHP to use at least the suggested minimum settings
190
+ */
191
+ private function do_raise_php_limits()
192
+ {
193
+ $php_limits = array(
194
+ 'memory_limit' => $this->memory_limit,
195
+ 'max_execution_time' => $this->max_execution_time,
196
+ 'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
197
+ 'pcre.recursion_limit' => $this->pcre_recursion_limit
198
+ );
199
+
200
+ // If current settings are higher respect them.
201
+ foreach ($php_limits as $name => $suggested) {
202
+ $current = $this->normalize_int(ini_get($name));
203
+ // memory_limit exception: allow -1 for "no memory limit".
204
+ if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
205
+ ini_set($name, $suggested);
206
+ }
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Does bulk of the minification
212
+ * @param string $css
213
+ * @param int|bool $linebreak_pos
214
+ * @return string
215
+ */
216
+ private function minify($css, $linebreak_pos)
217
+ {
218
+ // strings are safe, now wrestle the comments
219
+ for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
220
+
221
+ $token = $this->comments[$i];
222
+ $placeholder = '/' . self::COMMENT . $i . '___/';
223
+
224
+ // ! in the first position of the comment means preserve
225
+ // so push to the preserved tokens keeping the !
226
+ if (substr($token, 0, 1) === '!') {
227
+ $this->preserved_tokens[] = $token;
228
+ $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
229
+ $css = preg_replace($placeholder, $token_tring, $css, 1);
230
+ // Preserve new lines for /*! important comments
231
+ $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
232
+ $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
233
+ continue;
234
+ }
235
+
236
+ // \ in the last position looks like hack for Mac/IE5
237
+ // shorten that to /*\*/ and the next one to /**/
238
+ if (substr($token, (strlen($token) - 1), 1) === '\\') {
239
+ $this->preserved_tokens[] = '\\';
240
+ $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
241
+ $i = $i + 1; // attn: advancing the loop
242
+ $this->preserved_tokens[] = '';
243
+ $css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
244
+ continue;
245
+ }
246
+
247
+ // keep empty comments after child selectors (IE7 hack)
248
+ // e.g. html >/**/ body
249
+ if (strlen($token) === 0) {
250
+ $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
251
+ if ($start_index > 2) {
252
+ if (substr($css, $start_index - 3, 1) === '>') {
253
+ $this->preserved_tokens[] = '';
254
+ $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
255
+ }
256
+ }
257
+ }
258
+
259
+ // in all other cases kill the comment
260
+ $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
261
+ }
262
+
263
+
264
+ // Normalize all whitespace strings to single spaces. Easier to work with that way.
265
+ $css = preg_replace('/\s+/', ' ', $css);
266
+
267
+ // Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
268
+ $css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
269
+
270
+ // Shorten & preserve calculations calc(...) since spaces are important
271
+ $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
272
+
273
+ // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
274
+ // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
275
+ $css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
276
+
277
+ // Remove leading zeros from integer and float numbers preceded by : or a white-space
278
+ // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
279
+ $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
280
+
281
+ // Remove trailing zeros from float numbers preceded by : or a white-space
282
+ // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
283
+ $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
284
+
285
+ // Remove trailing .0 -> -9.0 to -9
286
+ $css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
287
+
288
+ // Replace 0 length numbers with 0
289
+ $css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
290
+
291
+ // Remove the spaces before the things that should not have spaces before them.
292
+ // But, be careful not to turn "p :link {...}" into "p:link{...}"
293
+ // Swap out any pseudo-class colons with the token, and then swap back.
294
+ $css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
295
+
296
+ // Remove spaces before the things that should not have spaces before them.
297
+ $css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
298
+
299
+ // Restore spaces for !important
300
+ $css = preg_replace('/\!important/i', ' !important', $css);
301
+
302
+ // bring back the colon
303
+ $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
304
+
305
+ // retain space for special IE6 cases
306
+ $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
307
+
308
+ // no space after the end of a preserved comment
309
+ $css = preg_replace('/\*\/ /', '*/', $css);
310
+
311
+ // lowercase some popular @directives
312
+ $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
313
+
314
+ // lowercase some more common pseudo-elements
315
+ $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
316
+
317
+ // lowercase some more common functions
318
+ $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
319
+
320
+ // lower case some common function that can be values
321
+ // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
322
+ $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
323
+
324
+ // Put the space back in some cases, to support stuff like
325
+ // @media screen and (-webkit-min-device-pixel-ratio:0){
326
+ $css = preg_replace('/\band\(/i', 'and (', $css);
327
+
328
+ // Remove the spaces after the things that should not have spaces after them.
329
+ $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
330
+
331
+ // remove unnecessary semicolons
332
+ $css = preg_replace('/;+\}/', '}', $css);
333
+
334
+ // Fix for issue: #2528146
335
+ // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
336
+ // to avoid issues on Symbian S60 3.x browsers.
337
+ $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
338
+
339
+ // Replace 0 <length> and 0 <percentage> values with 0.
340
+ // <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
341
+ // <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
342
+ $css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
343
+
344
+ // 0% step in a keyframe? restore the % unit
345
+ $css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
346
+
347
+ // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
348
+ $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
349
+
350
+ // Fix for issue: #2528142
351
+ // Replace text-shadow:0; with text-shadow:0 0 0;
352
+ $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
353
+
354
+ // Replace background-position:0; with background-position:0 0;
355
+ // same for transform-origin
356
+ // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
357
+ $css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
358
+
359
+ // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
360
+ // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
361
+ // This makes it more likely that it'll get further compressed in the next step.
362
+ $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
363
+ $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
364
+
365
+ // Shorten colors from #AABBCC to #ABC or short color name.
366
+ $css = $this->compress_hex_colors($css);
367
+
368
+ // border: none to border:0, outline: none to outline:0
369
+ $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
370
+
371
+ // shorter opacity IE filter
372
+ $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
373
+
374
+ // Find a fraction that is used for Opera's -o-device-pixel-ratio query
375
+ // Add token to add the "\" back in later
376
+ $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
377
+
378
+ // Remove empty rules.
379
+ $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
380
+
381
+ // Add "/" back to fix Opera -o-device-pixel-ratio query
382
+ $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
383
+
384
+ // Replace multiple semi-colons in a row by a single one
385
+ // See SF bug #1980989
386
+ $css = preg_replace('/;;+/', ';', $css);
387
+
388
+ // Restore new lines for /*! important comments
389
+ $css = preg_replace('/'. self::NL .'/', "\n", $css);
390
+
391
+ // Lowercase all uppercase properties
392
+ $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
393
+
394
+ // Some source control tools don't like it when files containing lines longer
395
+ // than, say 8000 characters, are checked in. The linebreak option is used in
396
+ // that case to split long lines after a specific column.
397
+ if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
398
+ $linebreak_pos = (int) $linebreak_pos;
399
+ $start_index = $i = 0;
400
+ while ($i < strlen($css)) {
401
+ $i++;
402
+ if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
403
+ $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
404
+ $start_index = $i;
405
+ }
406
+ }
407
+ }
408
+
409
+ // restore preserved comments and strings in reverse order
410
+ for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
411
+ $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
412
+ }
413
+
414
+ // Trim the final string (for any leading or trailing white spaces)
415
+ return trim($css);
416
+ }
417
+
418
+ /**
419
+ * Utility method to replace all data urls with tokens before we start
420
+ * compressing, to avoid performance issues running some of the subsequent
421
+ * regexes against large strings chunks.
422
+ *
423
+ * @param string $css
424
+ * @return string
425
+ */
426
+ private function extract_data_urls($css)
427
+ {
428
+ // Leave data urls alone to increase parse performance.
429
+ $max_index = strlen($css) - 1;
430
+ $append_index = $index = $last_index = $offset = 0;
431
+ $sb = array();
432
+ $pattern = '/url\(\s*(["\']?)data\:/i';
433
+
434
+ // Since we need to account for non-base64 data urls, we need to handle
435
+ // ' and ) being part of the data string. Hence switching to indexOf,
436
+ // to determine whether or not we have matching string terminators and
437
+ // handling sb appends directly, instead of using matcher.append* methods.
438
+
439
+ while (preg_match($pattern, $css, $m, 0, $offset)) {
440
+ $index = $this->index_of($css, $m[0], $offset);
441
+ $last_index = $index + strlen($m[0]);
442
+ $start_index = $index + 4; // "url(".length()
443
+ $end_index = $last_index - 1;
444
+ $terminator = $m[1]; // ', " or empty (not quoted)
445
+ $found_terminator = FALSE;
446
+
447
+ if (strlen($terminator) === 0) {
448
+ $terminator = ')';
449
+ }
450
+
451
+ while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
452
+ $end_index = $this->index_of($css, $terminator, $end_index + 1);
453
+
454
+ // endIndex == 0 doesn't really apply here
455
+ if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
456
+ $found_terminator = TRUE;
457
+ if (')' != $terminator) {
458
+ $end_index = $this->index_of($css, ')', $end_index);
459
+ }
460
+ }
461
+ }
462
+
463
+ // Enough searching, start moving stuff over to the buffer
464
+ $sb[] = $this->str_slice($css, $append_index, $index);
465
+
466
+ if ($found_terminator) {
467
+ $token = $this->str_slice($css, $start_index, $end_index);
468
+ $token = preg_replace('/\s+/', '', $token);
469
+ $this->preserved_tokens[] = $token;
470
+
471
+ $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
472
+ $sb[] = $preserver;
473
+
474
+ $append_index = $end_index + 1;
475
+ } else {
476
+ // No end terminator found, re-add the whole match. Should we throw/warn here?
477
+ $sb[] = $this->str_slice($css, $index, $last_index);
478
+ $append_index = $last_index;
479
+ }
480
+
481
+ $offset = $last_index;
482
+ }
483
+
484
+ $sb[] = $this->str_slice($css, $append_index);
485
+
486
+ return implode('', $sb);
487
+ }
488
+
489
+ /**
490
+ * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
491
+ *
492
+ * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
493
+ * e.g. #AddressForm { ... }
494
+ *
495
+ * DOES NOT compress IE filters, which have hex color values (which would break things).
496
+ * e.g. filter: chroma(color="#FFFFFF");
497
+ *
498
+ * DOES NOT compress invalid hex values.
499
+ * e.g. background-color: #aabbccdd
500
+ *
501
+ * @param string $css
502
+ * @return string
503
+ */
504
+ private function compress_hex_colors($css)
505
+ {
506
+ // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
507
+ $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
508
+ $_index = $index = $last_index = $offset = 0;
509
+ $sb = array();
510
+ // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
511
+ $short_safe = array(
512
+ '#808080' => 'gray',
513
+ '#008000' => 'green',
514
+ '#800000' => 'maroon',
515
+ '#000080' => 'navy',
516
+ '#808000' => 'olive',
517
+ '#ffa500' => 'orange',
518
+ '#800080' => 'purple',
519
+ '#c0c0c0' => 'silver',
520
+ '#008080' => 'teal',
521
+ '#f00' => 'red'
522
+ );
523
+
524
+ while (preg_match($pattern, $css, $m, 0, $offset)) {
525
+ $index = $this->index_of($css, $m[0], $offset);
526
+ $last_index = $index + strlen($m[0]);
527
+ $is_filter = $m[1] !== null && $m[1] !== '';
528
+
529
+ $sb[] = $this->str_slice($css, $_index, $index);
530
+
531
+ if ($is_filter) {
532
+ // Restore, maintain case, otherwise filter will break
533
+ $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
534
+ } else {
535
+ if (strtolower($m[2]) == strtolower($m[3]) &&
536
+ strtolower($m[4]) == strtolower($m[5]) &&
537
+ strtolower($m[6]) == strtolower($m[7])) {
538
+ // Compress.
539
+ $hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
540
+ } else {
541
+ // Non compressible color, restore but lower case.
542
+ $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
543
+ }
544
+ // replace Hex colors to short safe color names
545
+ $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
546
+ }
547
+
548
+ $_index = $offset = $last_index - strlen($m[8]);
549
+ }
550
+
551
+ $sb[] = $this->str_slice($css, $_index);
552
+
553
+ return implode('', $sb);
554
+ }
555
+
556
+ /* CALLBACKS
557
+ * ---------------------------------------------------------------------------------------------
558
+ */
559
+
560
+ private function replace_string($matches)
561
+ {
562
+ $match = $matches[0];
563
+ $quote = substr($match, 0, 1);
564
+ // Must use addcslashes in PHP to avoid parsing of backslashes
565
+ $match = addcslashes($this->str_slice($match, 1, -1), '\\');
566
+
567
+ // maybe the string contains a comment-like substring?
568
+ // one, maybe more? put'em back then
569
+ if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
570
+ for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
571
+ $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
572
+ }
573
+ }
574
+
575
+ // minify alpha opacity in filter strings
576
+ $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
577
+
578
+ $this->preserved_tokens[] = $match;
579
+ return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
580
+ }
581
+
582
+ private function replace_colon($matches)
583
+ {
584
+ return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
585
+ }
586
+
587
+ private function replace_calc($matches)
588
+ {
589
+ $this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
590
+ return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
591
+ }
592
+
593
+ private function preserve_old_IE_specific_matrix_definition($matches)
594
+ {
595
+ $this->preserved_tokens[] = $matches[1];
596
+ return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
597
+ }
598
+
599
+ private function replace_keyframe_zero($matches)
600
+ {
601
+ return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
602
+ }
603
+
604
+ private function rgb_to_hex($matches)
605
+ {
606
+ // Support for percentage values rgb(100%, 0%, 45%);
607
+ if ($this->index_of($matches[1], '%') >= 0){
608
+ $rgbcolors = explode(',', str_replace('%', '', $matches[1]));
609
+ for ($i = 0; $i < count($rgbcolors); $i++) {
610
+ $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
611
+ }
612
+ } else {
613
+ $rgbcolors = explode(',', $matches[1]);
614
+ }
615
+
616
+ // Values outside the sRGB color space should be clipped (0-255)
617
+ for ($i = 0; $i < count($rgbcolors); $i++) {
618
+ $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
619
+ $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
620
+ }
621
+
622
+ // Fix for issue #2528093
623
+ if (!preg_match('/[\s\,\);\}]/', $matches[2])){
624
+ $matches[2] = ' ' . $matches[2];
625
+ }
626
+
627
+ return '#' . implode('', $rgbcolors) . $matches[2];
628
+ }
629
+
630
+ private function hsl_to_hex($matches)
631
+ {
632
+ $values = explode(',', str_replace('%', '', $matches[1]));
633
+ $h = floatval($values[0]);
634
+ $s = floatval($values[1]);
635
+ $l = floatval($values[2]);
636
+
637
+ // Wrap and clamp, then fraction!
638
+ $h = ((($h % 360) + 360) % 360) / 360;
639
+ $s = $this->clamp_number($s, 0, 100) / 100;
640
+ $l = $this->clamp_number($l, 0, 100) / 100;
641
+
642
+ if ($s == 0) {
643
+ $r = $g = $b = $this->round_number(255 * $l);
644
+ } else {
645
+ $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
646
+ $v1 = (2 * $l) - $v2;
647
+ $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
648
+ $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
649
+ $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
650
+ }
651
+
652
+ return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
653
+ }
654
+
655
+ private function lowercase_pseudo_first($matches)
656
+ {
657
+ return ':first-'. strtolower($matches[1]) .' '. $matches[2];
658
+ }
659
+
660
+ private function lowercase_directives($matches)
661
+ {
662
+ return '@'. strtolower($matches[1]);
663
+ }
664
+
665
+ private function lowercase_pseudo_elements($matches)
666
+ {
667
+ return ':'. strtolower($matches[1]);
668
+ }
669
+
670
+ private function lowercase_common_functions($matches)
671
+ {
672
+ return ':'. strtolower($matches[1]) .'(';
673
+ }
674
+
675
+ private function lowercase_common_functions_values($matches)
676
+ {
677
+ return $matches[1] . strtolower($matches[2]);
678
+ }
679
+
680
+ private function lowercase_properties($matches)
681
+ {
682
+ return $matches[1].strtolower($matches[2]).$matches[3];
683
+ }
684
+
685
+ /* HELPERS
686
+ * ---------------------------------------------------------------------------------------------
687
+ */
688
+
689
+ private function hue_to_rgb($v1, $v2, $vh)
690
+ {
691
+ $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
692
+ if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
693
+ if ($vh * 2 < 1) return $v2;
694
+ if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
695
+ return $v1;
696
+ }
697
+
698
+ private function round_number($n)
699
+ {
700
+ return intval(floor(floatval($n) + 0.5), 10);
701
+ }
702
+
703
+ private function clamp_number($n, $min, $max)
704
+ {
705
+ return min(max($n, $min), $max);
706
+ }
707
+
708
+ /**
709
+ * PHP port of Javascript's "indexOf" function for strings only
710
+ * Author: Tubal Martin http://blog.margenn.com
711
+ *
712
+ * @param string $haystack
713
+ * @param string $needle
714
+ * @param int $offset index (optional)
715
+ * @return int
716
+ */
717
+ private function index_of($haystack, $needle, $offset = 0)
718
+ {
719
+ $index = strpos($haystack, $needle, $offset);
720
+
721
+ return ($index !== FALSE) ? $index : -1;
722
+ }
723
+
724
+ /**
725
+ * PHP port of Javascript's "slice" function for strings only
726
+ * Author: Tubal Martin http://blog.margenn.com
727
+ * Tests: http://margenn.com/tubal/str_slice/
728
+ *
729
+ * @param string $str
730
+ * @param int $start index
731
+ * @param int|bool $end index (optional)
732
+ * @return string
733
+ */
734
+ private function str_slice($str, $start = 0, $end = FALSE)
735
+ {
736
+ if ($end !== FALSE && ($start < 0 || $end <= 0)) {
737
+ $max = strlen($str);
738
+
739
+ if ($start < 0) {
740
+ if (($start = $max + $start) < 0) {
741
+ return '';
742
+ }
743
+ }
744
+
745
+ if ($end < 0) {
746
+ if (($end = $max + $end) < 0) {
747
+ return '';
748
+ }
749
+ }
750
+
751
+ if ($end <= $start) {
752
+ return '';
753
+ }
754
+ }
755
+
756
+ $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
757
+ return ($slice === FALSE) ? '' : $slice;
758
+ }
759
+
760
+ /**
761
+ * Convert strings like "64M" or "30" to int values
762
+ * @param mixed $size
763
+ * @return int
764
+ */
765
+ private function normalize_int($size)
766
+ {
767
+ if (is_string($size)) {
768
+ switch (substr($size, -1)) {
769
+ case 'M': case 'm': return $size * 1048576;
770
+ case 'K': case 'k': return $size * 1024;
771
+ case 'G': case 'g': return $size * 1073741824;
772
+ }
773
+ }
774
+
775
+ return (int) $size;
776
+ }
777
+ }
inc/minification/minify/yui-php-cssmin-2.4.8-4_fgo.php ADDED
@@ -0,0 +1,789 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*!
4
+ * cssmin.php v2.4.8-4
5
+ * Author: Tubal Martin - http://tubalmartin.me/
6
+ * Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
7
+ *
8
+ * This is a PHP port of the CSS minification tool distributed with YUICompressor,
9
+ * itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
10
+ * Permission is hereby granted to use the PHP version under the same
11
+ * conditions as the YUICompressor.
12
+ */
13
+
14
+ /*!
15
+ * YUI Compressor
16
+ * http://developer.yahoo.com/yui/compressor/
17
+ * Author: Julien Lecomte - http://www.julienlecomte.net/
18
+ * Copyright (c) 2013 Yahoo! Inc. All rights reserved.
19
+ * The copyrights embodied in the content of this file are licensed
20
+ * by Yahoo! Inc. under the BSD (revised) open source license.
21
+ */
22
+
23
+ class CSSmin
24
+ {
25
+ const NL = '___YUICSSMIN_PRESERVED_NL___';
26
+ const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
27
+ const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
28
+ const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
29
+ const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
30
+
31
+ private $comments;
32
+ private $preserved_tokens;
33
+ private $memory_limit;
34
+ private $max_execution_time;
35
+ private $pcre_backtrack_limit;
36
+ private $pcre_recursion_limit;
37
+ private $raise_php_limits;
38
+
39
+ /**
40
+ * @param bool|int $raise_php_limits
41
+ * If true, PHP settings will be raised if needed
42
+ */
43
+ public function __construct($raise_php_limits = TRUE)
44
+ {
45
+ // Set suggested PHP limits
46
+ $this->memory_limit = 128 * 1048576; // 128MB in bytes
47
+ $this->max_execution_time = 60; // 1 min
48
+ $this->pcre_backtrack_limit = 1000 * 1000;
49
+ $this->pcre_recursion_limit = 500 * 1000;
50
+
51
+ $this->raise_php_limits = (bool) $raise_php_limits;
52
+ }
53
+
54
+ /**
55
+ * Minify a string of CSS
56
+ * @param string $css
57
+ * @param int|bool $linebreak_pos
58
+ * @return string
59
+ */
60
+ public function run($css = '', $linebreak_pos = FALSE)
61
+ {
62
+ if (empty($css)) {
63
+ return '';
64
+ }
65
+
66
+ if ($this->raise_php_limits) {
67
+ $this->do_raise_php_limits();
68
+ }
69
+
70
+ $this->comments = array();
71
+ $this->preserved_tokens = array();
72
+
73
+ $start_index = 0;
74
+ $length = strlen($css);
75
+
76
+ $css = $this->extract_data_urls($css);
77
+
78
+ // collect all comment blocks...
79
+ while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
80
+ $end_index = $this->index_of($css, '*/', $start_index + 2);
81
+ if ($end_index < 0) {
82
+ $end_index = $length;
83
+ }
84
+ $comment_found = $this->str_slice($css, $start_index + 2, $end_index);
85
+ $this->comments[] = $comment_found;
86
+ $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
87
+ $css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
88
+ // Set correct start_index: Fixes issue #2528130
89
+ $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
90
+ }
91
+
92
+ // preserve strings so their content doesn't get accidentally minified
93
+ $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
94
+
95
+ // Let's divide css code in chunks of 5.000 chars aprox.
96
+ // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
97
+ // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
98
+ // long strings and a (sub)pattern matches a number of chars greater than
99
+ // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
100
+ // returning NULL and $css would be empty.
101
+ $charset = '';
102
+ $charset_regexp = '/(@charset)( [^;]+;)/i';
103
+ $css_chunks = array();
104
+ $css_chunk_length = 5000; // aprox size, not exact
105
+ $start_index = 0;
106
+ $i = $css_chunk_length; // save initial iterations
107
+ $l = strlen($css);
108
+
109
+
110
+ // if the number of characters is 5000 or less, do not chunk
111
+ if ($l <= $css_chunk_length) {
112
+ $css_chunks[] = $css;
113
+ } else {
114
+ // chunk css code securely
115
+ while ($i < $l) {
116
+ $i += 50; // save iterations
117
+ if ($l - $start_index <= $css_chunk_length || $i >= $l) {
118
+ $css_chunks[] = $this->str_slice($css, $start_index);
119
+ break;
120
+ }
121
+ if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
122
+ // If there are two ending curly braces }} separated or not by spaces,
123
+ // join them in the same chunk (i.e. @media blocks)
124
+ $next_chunk = substr($css, $i);
125
+ if (preg_match('/^\s*\}/', $next_chunk)) {
126
+ $i = $i + $this->index_of($next_chunk, '}') + 1;
127
+ }
128
+
129
+ $css_chunks[] = $this->str_slice($css, $start_index, $i);
130
+ $start_index = $i;
131
+ }
132
+ }
133
+ }
134
+
135
+ // Minify each chunk
136
+ for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
137
+ $css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
138
+ // Keep the first @charset at-rule found
139
+ if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
140
+ $charset = strtolower($matches[1]) . $matches[2];
141
+ }
142
+ // Delete all @charset at-rules
143
+ $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
144
+ }
145
+
146
+ // Update the first chunk and push the charset to the top of the file.
147
+ $css_chunks[0] = $charset . $css_chunks[0];
148
+
149
+ return implode('', $css_chunks);
150
+ }
151
+
152
+ /**
153
+ * Sets the memory limit for this script
154
+ * @param int|string $limit
155
+ */
156
+ public function set_memory_limit($limit)
157
+ {
158
+ $this->memory_limit = $this->normalize_int($limit);
159
+ }
160
+
161
+ /**
162
+ * Sets the maximum execution time for this script
163
+ * @param int|string $seconds
164
+ */
165
+ public function set_max_execution_time($seconds)
166
+ {
167
+ $this->max_execution_time = (int) $seconds;
168
+ }
169
+
170
+ /**
171
+ * Sets the PCRE backtrack limit for this script
172
+ * @param int $limit
173
+ */
174
+ public function set_pcre_backtrack_limit($limit)
175
+ {
176
+ $this->pcre_backtrack_limit = (int) $limit;
177
+ }
178
+
179
+ /**
180
+ * Sets the PCRE recursion limit for this script
181
+ * @param int $limit
182
+ */
183
+ public function set_pcre_recursion_limit($limit)
184
+ {
185
+ $this->pcre_recursion_limit = (int) $limit;
186
+ }
187
+
188
+ /**
189
+ * Try to configure PHP to use at least the suggested minimum settings
190
+ */
191
+ private function do_raise_php_limits()
192
+ {
193
+ $php_limits = array(
194
+ 'memory_limit' => $this->memory_limit,
195
+ 'max_execution_time' => $this->max_execution_time,
196
+ 'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
197
+ 'pcre.recursion_limit' => $this->pcre_recursion_limit
198
+ );
199
+
200
+ // If current settings are higher respect them.
201
+ foreach ($php_limits as $name => $suggested) {
202
+ $current = $this->normalize_int(ini_get($name));
203
+ // memory_limit exception: allow -1 for "no memory limit".
204
+ if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
205
+ ini_set($name, $suggested);
206
+ }
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Does bulk of the minification
212
+ * @param string $css
213
+ * @param int|bool $linebreak_pos
214
+ * @return string
215
+ */
216
+ private function minify($css, $linebreak_pos)
217
+ {
218
+ // strings are safe, now wrestle the comments
219
+ for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
220
+
221
+ $token = $this->comments[$i];
222
+ $placeholder = '/' . self::COMMENT . $i . '___/';
223
+
224
+ // ! in the first position of the comment means preserve
225
+ // so push to the preserved tokens keeping the !
226
+ if (substr($token, 0, 1) === '!') {
227
+ $this->preserved_tokens[] = $token;
228
+ $token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
229
+ $css = preg_replace($placeholder, $token_tring, $css, 1);
230
+ // Preserve new lines for /*! important comments
231
+ $css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
232
+ $css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
233
+ continue;
234
+ }
235
+
236
+ // \ in the last position looks like hack for Mac/IE5
237
+ // shorten that to /*\*/ and the next one to /**/
238
+ if (substr($token, (strlen($token) - 1), 1) === '\\') {
239
+ $this->preserved_tokens[] = '\\';
240
+ $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
241
+ $i = $i + 1; // attn: advancing the loop
242
+ $this->preserved_tokens[] = '';
243
+ $css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
244
+ continue;
245
+ }
246
+
247
+ // keep empty comments after child selectors (IE7 hack)
248
+ // e.g. html >/**/ body
249
+ if (strlen($token) === 0) {
250
+ $start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
251
+ if ($start_index > 2) {
252
+ if (substr($css, $start_index - 3, 1) === '>') {
253
+ $this->preserved_tokens[] = '';
254
+ $css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
255
+ }
256
+ }
257
+ }
258
+
259
+ // in all other cases kill the comment
260
+ $css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
261
+ }
262
+
263
+
264
+ // Normalize all whitespace strings to single spaces. Easier to work with that way.
265
+ $css = preg_replace('/\s+/', ' ', $css);
266
+
267
+ // preserve flex, keeping percentage even if 0
268
+ $css = preg_replace_callback('/flex\s?:\s?((?:[0-9 ]*)\s?(?:px|em|auto|%)?(?:calc\(.*\))?)/i',array($this, 'replace_flex'),$css);
269
+
270
+ // Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
271
+ $css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
272
+
273
+ // Shorten & preserve calculations calc(...) since spaces are important
274
+ $css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
275
+
276
+ // Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
277
+ // +1.2em to 1.2em, +.8px to .8px, +2% to 2%
278
+ $css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
279
+
280
+ // Remove leading zeros from integer and float numbers preceded by : or a white-space
281
+ // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
282
+ $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
283
+
284
+ // Remove trailing zeros from float numbers preceded by : or a white-space
285
+ // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
286
+ $css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
287
+
288
+ // Remove trailing .0 -> -9.0 to -9
289
+ $css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
290
+
291
+ // Replace 0 length numbers with 0
292
+ $css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
293
+
294
+ // Remove the spaces before the things that should not have spaces before them.
295
+ // But, be careful not to turn "p :link {...}" into "p:link{...}"
296
+ // Swap out any pseudo-class colons with the token, and then swap back.
297
+ $css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
298
+
299
+ // Remove spaces before the things that should not have spaces before them.
300
+ $css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
301
+
302
+ // Restore spaces for !important
303
+ $css = preg_replace('/\!important/i', ' !important', $css);
304
+
305
+ // bring back the colon
306
+ $css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
307
+
308
+ // retain space for special IE6 cases
309
+ $css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
310
+
311
+ // no space after the end of a preserved comment
312
+ $css = preg_replace('/\*\/ /', '*/', $css);
313
+
314
+ // lowercase some popular @directives
315
+ $css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
316
+
317
+ // lowercase some more common pseudo-elements
318
+ $css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
319
+
320
+ // lowercase some more common functions
321
+ $css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
322
+
323
+ // lower case some common function that can be values
324
+ // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
325
+ $css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
326
+
327
+ // Put the space back in some cases, to support stuff like
328
+ // @media screen and (-webkit-min-device-pixel-ratio:0){
329
+ $css = preg_replace('/\band\(/i', 'and (', $css);
330
+
331
+ // Remove the spaces after the things that should not have spaces after them.
332
+ $css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
333
+
334
+ // remove unnecessary semicolons
335
+ $css = preg_replace('/;+\}/', '}', $css);
336
+
337
+ // Fix for issue: #2528146
338
+ // Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
339
+ // to avoid issues on Symbian S60 3.x browsers.
340
+ $css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
341
+
342
+ // Replace 0 <length> and 0 <percentage> values with 0.
343
+ // <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
344
+ // <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
345
+ $css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
346
+
347
+ // 0% step in a keyframe? restore the % unit
348
+ $css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
349
+
350
+ // Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
351
+ $css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
352
+
353
+ // Fix for issue: #2528142
354
+ // Replace text-shadow:0; with text-shadow:0 0 0;
355
+ $css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
356
+
357
+ // Replace background-position:0; with background-position:0 0;
358
+ // same for transform-origin
359
+ // Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
360
+ $css = preg_replace('/(background|background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
361
+
362
+ // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
363
+ // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
364
+ // This makes it more likely that it'll get further compressed in the next step.
365
+ $css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
366
+ $css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
367
+
368
+ // Shorten colors from #AABBCC to #ABC or short color name.
369
+ $css = $this->compress_hex_colors($css);
370
+
371
+ // border: none to border:0, outline: none to outline:0
372
+ $css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
373
+
374
+ // shorter opacity IE filter
375
+ $css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
376
+
377
+ // Find a fraction that is used for Opera's -o-device-pixel-ratio query
378
+ // Add token to add the "\" back in later
379
+ $css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
380
+
381
+ // Remove empty rules.
382
+ $css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
383
+
384
+ // Add "/" back to fix Opera -o-device-pixel-ratio query
385
+ $css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
386
+
387
+ // Replace multiple semi-colons in a row by a single one
388
+ // See SF bug #1980989
389
+ $css = preg_replace('/;;+/', ';', $css);
390
+
391
+ // Restore new lines for /*! important comments
392
+ $css = preg_replace('/'. self::NL .'/', "\n", $css);
393
+
394
+ // Lowercase all uppercase properties
395
+ $css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
396
+
397
+ // Some source control tools don't like it when files containing lines longer
398
+ // than, say 8000 characters, are checked in. The linebreak option is used in
399
+ // that case to split long lines after a specific column.
400
+ if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
401
+ $linebreak_pos = (int) $linebreak_pos;
402
+ $start_index = $i = 0;
403
+ while ($i < strlen($css)) {
404
+ $i++;
405
+ if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
406
+ $css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
407
+ $start_index = $i;
408
+ }
409
+ }
410
+ }
411
+
412
+ // restore preserved comments and strings in reverse order
413
+ for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
414
+ $css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
415
+ // $css.=$this->preserved_tokens[$i];
416
+ }
417
+
418
+ // Trim the final string (for any leading or trailing white spaces)
419
+ return trim($css);
420
+ }
421
+
422
+ /**
423
+ * Utility method to replace all data urls with tokens before we start
424
+ * compressing, to avoid performance issues running some of the subsequent
425
+ * regexes against large strings chunks.
426
+ *
427
+ * @param string $css
428
+ * @return string
429
+ */
430
+ private function extract_data_urls($css)
431
+ {
432
+ // Leave data urls alone to increase parse performance.
433
+ $max_index = strlen($css) - 1;
434
+ $append_index = $index = $last_index = $offset = 0;
435
+ $sb = array();
436
+ $pattern = '/url\(\s*(["\']?)data\:/i';
437
+
438
+ // Since we need to account for non-base64 data urls, we need to handle
439
+ // ' and ) being part of the data string. Hence switching to indexOf,
440
+ // to determine whether or not we have matching string terminators and
441
+ // handling sb appends directly, instead of using matcher.append* methods.
442
+
443
+ while (preg_match($pattern, $css, $m, 0, $offset)) {
444
+ $index = $this->index_of($css, $m[0], $offset);
445
+ $last_index = $index + strlen($m[0]);
446
+ $start_index = $index + 4; // "url(".length()
447
+ $end_index = $last_index - 1;
448
+ $terminator = $m[1]; // ', " or empty (not quoted)
449
+ $found_terminator = FALSE;
450
+
451
+ if (strlen($terminator) === 0) {
452
+ $terminator = ')';
453
+ }
454
+
455
+ while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
456
+ $end_index = $this->index_of($css, $terminator, $end_index + 1);
457
+
458
+ // endIndex == 0 doesn't really apply here
459
+ if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
460
+ $found_terminator = TRUE;
461
+ if (')' != $terminator) {
462
+ $end_index = $this->index_of($css, ')', $end_index);
463
+ }
464
+ }
465
+ }
466
+
467
+ // Enough searching, start moving stuff over to the buffer
468
+ $sb[] = $this->str_slice($css, $append_index, $index);
469
+
470
+ if ($found_terminator) {
471
+ $token = $this->str_slice($css, $start_index, $end_index);
472
+ if (strpos($token,"<svg")===false && strpos($token,'svg+xml')===false) {
473
+ $token = preg_replace('/\s+/', '', $token);
474
+ }
475
+ $this->preserved_tokens[] = $token;
476
+
477
+ $preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
478
+ $sb[] = $preserver;
479
+
480
+ $append_index = $end_index + 1;
481
+ } else {
482
+ // No end terminator found, re-add the whole match. Should we throw/warn here?
483
+ $sb[] = $this->str_slice($css, $index, $last_index);
484
+ $append_index = $last_index;
485
+ }
486
+
487
+ $offset = $last_index;
488
+ }
489
+
490
+ $sb[] = $this->str_slice($css, $append_index);
491
+
492
+ return implode('', $sb);
493
+ }
494
+
495
+ /**
496
+ * Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
497
+ *
498
+ * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
499
+ * e.g. #AddressForm { ... }
500
+ *
501
+ * DOES NOT compress IE filters, which have hex color values (which would break things).
502
+ * e.g. filter: chroma(color="#FFFFFF");
503
+ *
504
+ * DOES NOT compress invalid hex values.
505
+ * e.g. background-color: #aabbccdd
506
+ *
507
+ * @param string $css
508
+ * @return string
509
+ */
510
+ private function compress_hex_colors($css)
511
+ {
512
+ // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
513
+ $pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
514
+ $_index = $index = $last_index = $offset = 0;
515
+ $sb = array();
516
+ // See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
517
+ $short_safe = array(
518
+ '#808080' => 'gray',
519
+ '#008000' => 'green',
520
+ '#800000' => 'maroon',
521
+ '#000080' => 'navy',
522
+ '#808000' => 'olive',
523
+ '#ffa500' => 'orange',
524
+ '#800080' => 'purple',
525
+ '#c0c0c0' => 'silver',
526
+ '#008080' => 'teal',
527
+ '#f00' => 'red'
528
+ );
529
+
530
+ while (preg_match($pattern, $css, $m, 0, $offset)) {
531
+ $index = $this->index_of($css, $m[0], $offset);
532
+ $last_index = $index + strlen($m[0]);
533
+ $is_filter = $m[1] !== null && $m[1] !== '';
534
+
535
+ $sb[] = $this->str_slice($css, $_index, $index);
536
+
537
+ if ($is_filter) {
538
+ // Restore, maintain case, otherwise filter will break
539
+ $sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
540
+ } else {
541
+ if (strtolower($m[2]) == strtolower($m[3]) &&
542
+ strtolower($m[4]) == strtolower($m[5]) &&
543
+ strtolower($m[6]) == strtolower($m[7])) {
544
+ // Compress.
545
+ $hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
546
+ } else {
547
+ // Non compressible color, restore but lower case.
548
+ $hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
549
+ }
550
+ // replace Hex colors to short safe color names
551
+ $sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
552
+ }
553
+
554
+ $_index = $offset = $last_index - strlen($m[8]);
555
+ }
556
+
557
+ $sb[] = $this->str_slice($css, $_index);
558
+
559
+ return implode('', $sb);
560
+ }
561
+
562
+ /* CALLBACKS
563
+ * ---------------------------------------------------------------------------------------------
564
+ */
565
+
566
+ private function replace_string($matches)
567
+ {
568
+ $match = $matches[0];
569
+ $quote = substr($match, 0, 1);
570
+ // Must use addcslashes in PHP to avoid parsing of backslashes
571
+ $match = addcslashes($this->str_slice($match, 1, -1), '\\');
572
+
573
+ // maybe the string contains a comment-like substring?
574
+ // one, maybe more? put'em back then
575
+ if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
576
+ for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
577
+ $match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
578
+ }
579
+ }
580
+
581
+ // minify alpha opacity in filter strings
582
+ $match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
583
+
584
+ $this->preserved_tokens[] = $match;
585
+ return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
586
+ }
587
+
588
+ private function replace_colon($matches)
589
+ {
590
+ return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
591
+ }
592
+
593
+ private function replace_calc($matches)
594
+ {
595
+ $this->preserved_tokens[] = preg_replace('/([\+\-]{1})\(/','$1 (',trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2])));
596
+ return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
597
+ }
598
+
599
+ private function replace_flex($matches)
600
+ {
601
+ $this->preserved_tokens[] = trim($matches[1]);
602
+ return 'flex:'.self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
603
+ }
604
+
605
+ private function preserve_old_IE_specific_matrix_definition($matches)
606
+ {
607
+ $this->preserved_tokens[] = $matches[1];
608
+ return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
609
+ }
610
+
611
+ private function replace_keyframe_zero($matches)
612
+ {
613
+ return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
614
+ }
615
+
616
+ private function rgb_to_hex($matches)
617
+ {
618
+ // Support for percentage values rgb(100%, 0%, 45%);
619
+ if ($this->index_of($matches[1], '%') >= 0){
620
+ $rgbcolors = explode(',', str_replace('%', '', $matches[1]));
621
+ for ($i = 0; $i < count($rgbcolors); $i++) {
622
+ $rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
623
+ }
624
+ } else {
625
+ $rgbcolors = explode(',', $matches[1]);
626
+ }
627
+
628
+ // Values outside the sRGB color space should be clipped (0-255)
629
+ for ($i = 0; $i < count($rgbcolors); $i++) {
630
+ $rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
631
+ $rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
632
+ }
633
+
634
+ // Fix for issue #2528093
635
+ if (!preg_match('/[\s\,\);\}]/', $matches[2])){
636
+ $matches[2] = ' ' . $matches[2];
637
+ }
638
+
639
+ return '#' . implode('', $rgbcolors) . $matches[2];
640
+ }
641
+
642
+ private function hsl_to_hex($matches)
643
+ {
644
+ $values = explode(',', str_replace('%', '', $matches[1]));
645
+ $h = floatval($values[0]);
646
+ $s = floatval($values[1]);
647
+ $l = floatval($values[2]);
648
+
649
+ // Wrap and clamp, then fraction!
650
+ $h = ((($h % 360) + 360) % 360) / 360;
651
+ $s = $this->clamp_number($s, 0, 100) / 100;
652
+ $l = $this->clamp_number($l, 0, 100) / 100;
653
+
654
+ if ($s == 0) {
655
+ $r = $g = $b = $this->round_number(255 * $l);
656
+ } else {
657
+ $v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
658
+ $v1 = (2 * $l) - $v2;
659
+ $r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
660
+ $g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
661
+ $b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
662
+ }
663
+
664
+ return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
665
+ }
666
+
667
+ private function lowercase_pseudo_first($matches)
668
+ {
669
+ return ':first-'. strtolower($matches[1]) .' '. $matches[2];
670
+ }
671
+
672
+ private function lowercase_directives($matches)
673
+ {
674
+ return '@'. strtolower($matches[1]);
675
+ }
676
+
677
+ private function lowercase_pseudo_elements($matches)
678
+ {
679
+ return ':'. strtolower($matches[1]);
680
+ }
681
+
682
+ private function lowercase_common_functions($matches)
683
+ {
684
+ return ':'. strtolower($matches[1]) .'(';
685
+ }
686
+
687
+ private function lowercase_common_functions_values($matches)
688
+ {
689
+ return $matches[1] . strtolower($matches[2]);
690
+ }
691
+
692
+ private function lowercase_properties($matches)
693
+ {
694
+ return $matches[1].strtolower($matches[2]).$matches[3];
695
+ }
696
+
697
+ /* HELPERS
698
+ * ---------------------------------------------------------------------------------------------
699
+ */
700
+
701
+ private function hue_to_rgb($v1, $v2, $vh)
702
+ {
703
+ $vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
704
+ if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
705
+ if ($vh * 2 < 1) return $v2;
706
+ if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
707
+ return $v1;
708
+ }
709
+
710
+ private function round_number($n)
711
+ {
712
+ return intval(floor(floatval($n) + 0.5), 10);
713
+ }
714
+
715
+ private function clamp_number($n, $min, $max)
716
+ {
717
+ return min(max($n, $min), $max);
718
+ }
719
+
720
+ /**
721
+ * PHP port of Javascript's "indexOf" function for strings only
722
+ * Author: Tubal Martin http://blog.margenn.com
723
+ *
724
+ * @param string $haystack
725
+ * @param string $needle
726
+ * @param int $offset index (optional)
727
+ * @return int
728
+ */
729
+ private function index_of($haystack, $needle, $offset = 0)
730
+ {
731
+ $index = strpos($haystack, $needle, $offset);
732
+
733
+ return ($index !== FALSE) ? $index : -1;
734
+ }
735
+
736
+ /**
737
+ * PHP port of Javascript's "slice" function for strings only
738
+ * Author: Tubal Martin http://blog.margenn.com
739
+ * Tests: http://margenn.com/tubal/str_slice/
740
+ *
741
+ * @param string $str
742
+ * @param int $start index
743
+ * @param int|bool $end index (optional)
744
+ * @return string
745
+ */
746
+ private function str_slice($str, $start = 0, $end = FALSE)
747
+ {
748
+ if ($end !== FALSE && ($start < 0 || $end <= 0)) {
749
+ $max = strlen($str);
750
+
751
+ if ($start < 0) {
752
+ if (($start = $max + $start) < 0) {
753
+ return '';
754
+ }
755
+ }
756
+
757
+ if ($end < 0) {
758
+ if (($end = $max + $end) < 0) {
759
+ return '';
760
+ }
761
+ }
762
+
763
+ if ($end <= $start) {
764
+ return '';
765
+ }
766
+ }
767
+
768
+ $slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
769
+ return ($slice === FALSE) ? '' : $slice;
770
+ }
771
+
772
+ /**
773
+ * Convert strings like "64M" or "30" to int values
774
+ * @param mixed $size
775
+ * @return int
776
+ */
777
+ private function normalize_int($size)
778
+ {
779
+ if (is_string($size)) {
780
+ switch (substr($size, -1)) {
781
+ case 'M': case 'm': return $size * 1048576;
782
+ case 'K': case 'k': return $size * 1024;
783
+ case 'G': case 'g': return $size * 1073741824;
784
+ }
785
+ }
786
+
787
+ return (int) $size;
788
+ }
789
+ }
languages/breeze-en_US.mo ADDED
Binary file
languages/breeze-en_US.po ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Cloudways Wordpress Cache 1.0\n"
4
+ "POT-Creation-Date: \n"
5
+ "PO-Revision-Date: \n"
6
+ "Last-Translator: \n"
7
+ "Language-Team: \n"
8
+ "Language: en_US\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "X-Generator: Poedit 1.8.9\n"
13
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
14
+
15
+ msgid "Cloudways WP Cache"
16
+ msgstr ""
17
+
18
+ msgid "Cloudways Purge Cache"
19
+ msgstr ""
20
+
21
+ msgid "Please set the IPs for Purge Cache!"
22
+ msgstr ""
23
+
24
+ msgid "Cloudways Varnish Purge Interval"
25
+ msgstr ""
26
+
27
+ msgid "Purge cache after"
28
+ msgstr ""
29
+
30
+ msgid "Set IPs"
31
+ msgstr ""
32
+
33
+ msgid "Save setting"
34
+ msgstr ""
35
+
36
+ msgid ""
37
+ msgstr ""
38
+
39
+ msgid ""
40
+ msgstr ""
41
+
42
+ msgid ""
43
+ msgstr ""
44
+
45
+ msgid ""
46
+ msgstr ""
47
+
48
+ msgid ""
49
+ msgstr ""
50
+
51
+ msgid ""
52
+ msgstr ""
53
+
54
+ msgid ""
55
+ msgstr ""
56
+
57
+ msgid ""
58
+ msgstr ""
59
+
60
+ msgid ""
61
+ msgstr ""
62
+
63
+ msgid ""
64
+ msgstr ""
65
+
66
+ msgid ""
67
+ msgstr ""
68
+
69
+ msgid ""
70
+ msgstr ""
71
+
72
+ msgid ""
73
+ msgstr ""
74
+
75
+ msgid ""
76
+ msgstr ""
77
+
78
+ msgid ""
79
+ msgstr ""
80
+
81
+ msgid ""
82
+ msgstr ""
83
+
84
+ msgid ""
85
+ msgstr ""
86
+
87
+ msgid ""
88
+ msgstr ""
89
+
90
+ msgid ""
91
+ msgstr ""
92
+
93
+ msgid ""
94
+ msgstr ""
95
+
96
+ msgid ""
97
+ msgstr ""
98
+
99
+ msgid ""
100
+ msgstr ""
101
+
102
+ msgid ""
103
+ msgstr ""
104
+
105
+ msgid ""
106
+ msgstr ""
107
+
108
+ msgid ""
109
+ msgstr ""
110
+
languages/breeze-fr_FR.mo ADDED
Binary file
languages/breeze-fr_FR.po ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Cloudways Wordpress Cache 1.0\n"
4
+ "POT-Creation-Date: \n"
5
+ "PO-Revision-Date: \n"
6
+ "Last-Translator: \n"
7
+ "Language-Team: \n"
8
+ "Language: fr_FR\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "X-Generator: Poedit 1.8.9\n"
13
+
14
+ msgid "Cloudways WP Cache"
15
+ msgstr ""
16
+
17
+ msgid "Cloudways Purge Cache"
18
+ msgstr ""
19
+
20
+ msgid "Please set the IPs for Purge Cache!"
21
+ msgstr ""
22
+
23
+ msgid "Cloudways Varnish Purge Interval"
24
+ msgstr ""
25
+
26
+ msgid "Purge cache after"
27
+ msgstr ""
28
+
29
+ msgid "Set IPs"
30
+ msgstr ""
31
+
32
+ msgid "Save setting"
33
+ msgstr ""
34
+
35
+ msgid ""
36
+ msgstr ""
37
+
38
+ msgid ""
39
+ msgstr ""
40
+
41
+ msgid ""
42
+ msgstr ""
43
+
44
+ msgid ""
45
+ msgstr ""
46
+
47
+ msgid ""
48
+ msgstr ""
49
+
50
+ msgid ""
51
+ msgstr ""
52
+
53
+ msgid ""
54
+ msgstr ""
55
+
56
+ msgid ""
57
+ msgstr ""
58
+
59
+ msgid ""
60
+ msgstr ""
61
+
62
+ msgid ""
63
+ msgstr ""
64
+
65
+ msgid ""
66
+ msgstr ""
67
+
68
+ msgid ""
69
+ msgstr ""
70
+
71
+ msgid ""
72
+ msgstr ""
73
+
74
+ msgid ""
75
+ msgstr ""
76
+
77
+ msgid ""
78
+ msgstr ""
79
+
80
+ msgid ""
81
+ msgstr ""
82
+
83
+ msgid ""
84
+ msgstr ""
85
+
86
+ msgid ""
87
+ msgstr ""
languages/breeze.pot ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #, fuzzy
2
+ msgid ""
3
+ msgstr ""
4
+ "Project-Id-Version: Cloudways Wordpress Cache\n"
5
+ "POT-Creation-Date: \n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: \n"
8
+ "Language-Team: \n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "Language: en_GB\n"
13
+ "X-Generator: Poedit 1.8.5\n"
14
+ "X-Poedit-SourceCharset: UTF-8\n"
15
+
16
+ msgid "Cloudways WP Cache"
17
+ msgstr "1"
18
+
19
+ msgid "Cloudways Purge Cache"
20
+ msgstr ""
21
+
22
+ msgid "Please set the IPs for Purge Cache!"
23
+ msgstr ""
24
+
25
+ msgid "Cloudways Varnish Purge Interval"
26
+ msgstr ""
27
+
28
+ msgid "Purge cache after"
29
+ msgstr ""
30
+
31
+ msgid "Set IPs"
32
+ msgstr ""
33
+
34
+ msgid "Save setting"
35
+ msgstr ""
36
+
37
+ msgid ""
38
+ msgstr ""
39
+
40
+ msgid ""
41
+ msgstr ""
42
+
43
+ msgid ""
44
+ msgstr ""
45
+
46
+ msgid ""
47
+ msgstr ""
48
+
49
+ msgid ""
50
+ msgstr ""
51
+
52
+ msgid ""
53
+ msgstr ""
54
+
55
+ msgid ""
56
+ msgstr ""
57
+
58
+ msgid ""
59
+ msgstr ""
60
+
61
+ msgid ""
62
+ msgstr ""
63
+
64
+ msgid ""
65
+ msgstr ""
66
+
67
+ msgid ""
68
+ msgstr ""
69
+
70
+ msgid ""
71
+ msgstr ""
72
+
73
+ msgid ""
74
+ msgstr ""
75
+
76
+ msgid ""
77
+ msgstr ""
78
+
79
+ msgid ""
80
+ msgstr ""
81
+
82
+ msgid ""
83
+ msgstr ""
84
+
85
+ msgid ""
86
+ msgstr ""
87
+
88
+ msgid ""
89
+ msgstr ""
90
+
91
+ msgid ""
92
+ msgstr ""
93
+
94
+ msgid ""
95
+ msgstr ""
96
+
97
+ msgid ""
98
+ msgstr ""
99
+
100
+ msgid ""
101
+ msgstr ""
102
+
103
+ msgid ""
104
+ msgstr ""
105
+
106
+ msgid ""
107
+ msgstr ""
108
+
109
+ msgid ""
110
+ msgstr ""
111
+
readme.txt ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Breeze - WordPress Cache Plugin ===
2
+ Contributors: Cloudways
3
+ Tags: cache, caching, performance, wp-cache, cdn, combine, compress, speed plugin, database cache,gzip, http compression, js cache, minify, optimize, page cache, performance, speed, expire headers
4
+ Requires at least: 4.5
5
+ Tested up to: 4.8
6
+ Stable tag: 1.0.0
7
+ License: GPLv2 or later
8
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
+
10
+ Breeze is a WordPress Caching Plugin developed by Cloudways. Breeze uses advance caching systems to improve WordPress loading times exponentially.
11
+
12
+ == Description ==
13
+
14
+ Breeze is a free, simple (yet powerful) and user-friendly WordPress Caching Plugin developed by the Cloudways team. It offers various options to optimize WordPress performance at various levels. It works equally great with WordPress, WordPress with WooCommerce and WordPress Multisite.
15
+
16
+ Breeze excels in the following areas:
17
+
18
+ * **Performance:** Breeze improves website speed and resource optimization. Other features include file level cache system, database cleanup, minification, support for Varnish cache and simplified CDN integration options.
19
+
20
+ * **Convenience:** Breeze is easy to install and configure directly from WordPress. Configuring Breeze is easy and most of the default options work well right out of the box. The recommended settings should work on all your WordPress websites seamlessly.
21
+
22
+ * **Simplicity:** Breeze is designed to be simple for all users. Just install and activate the plugin and you'll see the results instantaneously.
23
+
24
+ What makes Breeze WordPress Cache Plugin awesome is that it comes with builtin support for Varnish. If Varnish is not installed on your servers, Breeze will utilize its internal cache mechanism to boost up your WordPress site performance.
25
+
26
+ **Support:** We love to provide support! Post your questions on the WordPress.org support forums, or if you are a Cloudways Customer you may ask questions on the <a href="https://community.cloudways.com/">Cloudways Community Forum</a>.
27
+
28
+ **Note:** This plugin is still in Beta phase and this plugin is tested on Cloudways WordPress hosting servers.
29
+
30
+
31
+
32
+ == Installation ==
33
+
34
+ = To install the plugin via WordPress Dashboard: =
35
+ * In the WordPress admin panel, navigate to Plugin > Add new
36
+ * Search for Breeze
37
+ * Click install and wait for the installation to finish. Next, click the activate link
38
+
39
+ = To install the plugin manually: =
40
+ * Download and unzip the plugin package - breeze.1.0.0.zip
41
+ * Upload the breeze to /wp-content/plugins/
42
+ * Activate the plugin through the 'Plugins' menu in WordPress
43
+ * Access Breeze from WordPress Admin > Settings > Breeze
44
+
45
+ == Frequently Asked Questions ==
46
+
47
+ = Installation Instructions
48
+
49
+ To install the plugin via WordPress Dashboard
50
+ 1. In the WordPress admin panel, Menu > Plugin > Add new
51
+ 2. Search for Breeze
52
+ 3. Click on install and wait for the installation to finish. Next, then click on the activate link
53
+
54
+ To install the plugin manually
55
+ 1. Download and unzip the plugin package - breeze.1.0.0.zip
56
+ 2. Upload the /breeze to /wp-content/plugins/
57
+ 3. Activate the plugin through the 'Plugins' menu in WordPress
58
+ 4. Access Breeze from WordPress Admin > Settings > Breeze
59
+
60
+ = Does Breeze support Varnish and to what extent? =
61
+
62
+ Breeze, by default, supports Varnish. It has been tested to be fully compatible with Cloudways Servers that come with Varnish pre-installed. If you are using hosting providers other than Cloudways, we suggest you confirm Varnish support with your hosting provider
63
+
64
+ = Does Breeze support WooCommerce? =
65
+
66
+ Breeze is fully compatible with WooCommerce, out of the box. It does not require any special configurations.
67
+
68
+ = Does Breeze support WordPress Multisite? =
69
+
70
+ Breeze is fully compatible with WordPress Multisite without the need for any extra configuration.
71
+
72
+ = Is Breeze compatible with other WordPress Cache plugins? =
73
+
74
+ We DO NOT recommend using two WordPress cache plugins at the same time on any WordPress website.
75
+ We strongly recommend that you use Breeze as the only cache plugin for your website. If there are any other cache plugins installed, please ensure that you have disabled them prior to proceeding with the Breeze installation.
76
+
77
+
78
+ = Is Breeze compatible with HTTPS? =
79
+
80
+ Breeze does not require any special configuration to work with HTTP or HTTPS pages.
81
+
82
+ = Does Breeze have compatibility issues with other known plugins? =
83
+
84
+ Breeze has been tested with popular plugins available on WordPress.org. Please feel free to report any incompatibilities on the WordPress Support Forums or on <a href="https://community.cloudways.com/">Cloudways Community Forum</a>.
85
+
86
+ = Does Breeze support CDN? =
87
+
88
+ Breeze supports CDN integration. It allows all static assets (such as images, CSS and JS files) to be served via CDN.
89
+
90
+ = What does Breeze's Database Optimization feature do? =
91
+
92
+ WordPress databases are notorious for storing information like post revisions, spam comments and much more. Over time, databases l become bloated and it is a good practice to clear out unwanted information to reduce database size and improve optimization.
93
+
94
+ Breeze's database optimization cleans out unwanted information in a single click.
95
+
96
+ = Will comments and other dynamic parts of my blog appear immediately? =
97
+
98
+ Comments will appear upon moderation as per the comment system (or policy) set in place by the blog owner. Other dynamic changes such as any modifications in files will require a full cache purge.
99
+
100
+ = Can I exclude URLs of individual files and pages from cache? =
101
+
102
+ You can exclude a file by mentioning its URL or file type (by mentioning file extension) in the exclude fields (available in the Breeze settings). Exclude will not let the cache impact that URL or file type.
103
+
104
+ If Varnish is active, you will need to exclude URLs and file type(s) in the Varnish configuration. If you are hosting WordPress websites on Cloudways servers, follow <a href="https://support.cloudways.com/how-to-exclude-url-from-varnish/">this KB to exclude URLs from the Varnish cache</a>.
105
+
106
+
107
+ = Does it work with all hosting providers? =
108
+
109
+ Breeze is in Beta phase and has not yet been tested on hosting providers other than Cloudways.
110
+ However, major Breeze options such as Gzip, browser cache, minification, grouping, database optimization. CDN integration will work as expected on other hosting providers.
111
+
112
+
113
+ = Where can I get support for Breeze? =
114
+
115
+ You can get your questions answered on the WordPress support forums. If you are a Cloudways customer, please feel free to start a discussion at <a href="https://community.cloudways.com/">Cloudways Community Forum</a>.
116
+
117
+ == Changelog ==
118
+
119
+ = 1.0.0 =
120
+ * Add : First Beta release
121
+
122
+
123
+ == Upgrade Notice ==
124
+
125
+ Update Breeze through WordPress Admin > Dashboard >Updates. The settings will remain intact after the update.
126
+
127
+ == Screenshots ==
128
+
129
+
130
+ == Requirements ==
131
+
132
+ PHP 5.3+, PHP7 or 7.1 recommended for better performance, WordPress 4.5+
views/breeze-setting-views.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $tabs = array(
3
+ 'basic' => __('Basic options', 'breeze'),
4
+ 'advanced' => __('Advanced options', 'breeze'),
5
+ 'database' => __('Database', 'breeze'),
6
+ 'cdn' => __('CDN', 'breeze'),
7
+ 'varnish' => __('Varnish', 'breeze'),
8
+ );
9
+ ?>
10
+ <?php if (isset($_REQUEST['database-cleanup']) && $_REQUEST['database-cleanup'] == 'success'): ?>
11
+ <div id="message-save-settings" class="notice notice-success" style="margin: 10px 0px 10px 0;padding: 10px;"><strong><?php _e('Database cleanup successful', 'breeze'); ?></strong></div>
12
+ <?php endif; ?>
13
+ <!--save settings successfull message-->
14
+ <?php if (isset($_REQUEST['save-settings']) && $_REQUEST['save-settings'] == 'success'): ?>
15
+ <div id="message-save-settings" class="notice notice-success" style="margin: 10px 0px 10px 0;padding: 10px;"><strong><?php _e('Configuration settings saved', 'breeze'); ?></strong></div>
16
+ <?php endif; ?>
17
+ <div class="wrap breeze-main">
18
+ <div class="breeze-header" style="display: none"><a href="https://www.cloudways.com" target="_blank">
19
+ <div class="breeze-logo"></div>
20
+ <label class="breeze-logo-title"><?php _e('CLOUDWAYS', 'breeze'); ?></label>
21
+ </a>
22
+ </div>
23
+ <div style="clear: both"></div>
24
+ <h1 style="padding: 0;font-size: 30px"><?php _e("Breeze - WordPress Cache Settings", 'breeze'); ?></h1>
25
+
26
+ <ul id="breeze-tabs" class="nav-tab-wrapper">
27
+ <?php
28
+ foreach ($tabs as $key => $name) {
29
+ echo '<a id="tab-' . $key . '" class="nav-tab" href="#tab-' . $key . '" data-tab-id="' . $key . '"> ' . $name . ' </a> ';
30
+ }
31
+ ?>
32
+ </ul>
33
+
34
+ <div id="breeze-tabs-content" class="tab-content">
35
+ <?php
36
+ foreach ($tabs as $key => $name) {
37
+ echo '<div id="tab-content-' . $key . '" class="tab-pane">';
38
+ echo '<form class="breeze-form" method="post" action="">';
39
+ echo '<div class="tab-child">';
40
+ echo '<input type="hidden" name="breeze_'.$key.'_action" value="breeze_'.$key.'_settings">';
41
+ wp_nonce_field('breeze_settings_' . $key, 'breeze_settings_' . $key . '_nonce');
42
+ Breeze_Admin::render($key);
43
+ echo '</div>';
44
+ if ($key == 'database'){
45
+ echo '<p class="submit">
46
+ <input type="submit" class="button button-primary" value="Optimize"/>
47
+ </p>';
48
+ }else{
49
+ echo '<p class="submit">
50
+ <input type="submit" class="button button-primary" value="Save Changes"/>
51
+ </p>';
52
+ }
53
+ echo '</form>';
54
+ echo '</div>';
55
+
56
+ }
57
+ ?>
58
+ </div>
59
+ </div>
views/tabs/advanced.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $advanced = get_option('breeze_advanced_settings');
3
+ ?>
4
+ <table cellspacing="15">
5
+ <tr>
6
+ <td>
7
+ <label for="exclude-urls" class="breeze_tool_tip"><?php _e('Never Cache these URLs', 'breeze'); ?></label>
8
+ </td>
9
+ <td>
10
+ <textarea cols="100" rows="7" id="exclude-urls"
11
+ name="exclude-urls"><?php if (!empty($advanced['breeze-exclude-urls'])) {
12
+ $output = implode("\n", $advanced['breeze-exclude-urls']);
13
+ echo esc_textarea($output);
14
+ } ?></textarea>
15
+ <br>
16
+ <label class="breeze_tool_tip"><b>Note:&nbsp;</b><?php _e('Add URLs per line of pages that you want to exclude from the WordPress internal cache. To exclude URLs from Varnish cache, please refer to ', 'breeze') ?><a href="https://support.cloudways.com/how-to-exclude-url-from-varnish/" target="_blank"><?php _e('this KB','breeze')?></a></a> </label>
17
+ </td>
18
+ </tr>
19
+ <tr>
20
+ <td>
21
+ <label class="breeze_tool_tip"><?php _e('Group Files', 'breeze'); ?></label>
22
+ </td>
23
+ <td>
24
+ <ul>
25
+ <li>
26
+ <input type="checkbox" name="group-css" value="1" <?php checked($advanced['breeze-group-css'],'1')?>/>
27
+ <label class="breeze_tool_tip"><?php _e('CSS','breeze')?></label>
28
+ </li>
29
+ <li>
30
+ <input type="checkbox" name="group-js" value="1" <?php checked($advanced['breeze-group-js'],'1')?>/>
31
+ <label class="breeze_tool_tip"><?php _e('JS','breeze')?></label>
32
+ </li>
33
+ <li>
34
+ <label class="breeze_tool_tip">
35
+ <b>Note:&nbsp;</b><?php _e('Group CSS, JS files to combine them into single file. This will reduce the number of HTTP requests to your server.', 'breeze') ?><br>
36
+ <b><?php _e('Enable Minification to be able to use Group option','breeze')?></b>
37
+ </label>
38
+ </li>
39
+ </ul>
40
+ </td>
41
+ </tr>
42
+ <tr>
43
+ <td>
44
+ <label for="exclude-css" class="breeze_tool_tip"><?php _e('Exclude CSS', 'breeze') ?></label>
45
+ </td>
46
+ <td>
47
+ <textarea cols="100" rows="7" id="exclude-css"
48
+ name="exclude-css"><?php if (!empty($advanced['breeze-exclude-css'])) {
49
+ $output = implode("\n", $advanced['breeze-exclude-css']);
50
+ echo esc_textarea($output);
51
+ } ?></textarea>
52
+ <br>
53
+ <label class="breeze_tool_tip"><b>Note:&nbsp;</b><?php _e('Use this option to exclude CSS files from Minification and Grouping. Enter the URLs of CSS files on each line.', 'breeze') ?></label>
54
+ </td>
55
+ </tr>
56
+ <tr>
57
+ <td>
58
+ <label for="exclude-js" class="breeze_tool_tip"><?php _e('Exclude JS', 'breeze') ?></label>
59
+ </td>
60
+ <td>
61
+ <textarea cols="100" rows="7" id="exclude-js"
62
+ name="exclude-js"><?php if (!empty($advanced['breeze-exclude-js'])) {
63
+ $output = implode("\n", $advanced['breeze-exclude-js']);
64
+ echo esc_textarea($output);
65
+ } ?></textarea>
66
+ <br>
67
+ <label class="breeze_tool_tip"><b>Note:&nbsp;</b><?php _e('Use this option to exclude JS files from Minification and Grouping. Enter the URLs of JS files on each line.', 'breeze') ?></label>
68
+ </td>
69
+ </tr>
70
+ </table>
views/tabs/basic.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $basic = get_option('breeze_basic_settings');
3
+ ?>
4
+ <table cellspacing="15">
5
+ <tr>
6
+ <td>
7
+ <label for="cache-system"><?php _e('Cache System', 'breeze'); ?></label>
8
+ </td>
9
+ <td>
10
+ <input type="checkbox" id="cache-system" name="cache-system"
11
+ value='1' <?php checked($basic['breeze-active'], '1') ?>/>
12
+ <label class="breeze_tool_tip">
13
+ <?php _e('This is the basic cache that we recommend should be kept enabled in all cases. Basic cache will build the internal and static caches for the WordPress websites.', 'breeze') ?>
14
+ </label>
15
+ </td>
16
+ </tr>
17
+ <tr>
18
+ <td>
19
+ <label for="cache-ttl"><?php _e('Purge cache after', 'breeze'); ?></label>
20
+ </td>
21
+ <td>
22
+ <input type="text" id="cache-ttl" size="5" name="cache-ttl"
23
+ value='<?php echo(!empty($basic['breeze-ttl']) ? (int)$basic['breeze-ttl'] : '1440'); ?>'/>
24
+ <label class="breeze_tool_tip" style="vertical-align: baseline">
25
+ <?php _e('Automatically purge internal cache after X minutes. By default this is set to 1440 minutes', 'breeze') ?>
26
+ </label>
27
+ </td>
28
+ </tr>
29
+ <tr>
30
+ <td>
31
+ <label class="breeze_tool_tip"><?php _e('Minification', 'breeze'); ?></label>
32
+ </td>
33
+ <td>
34
+ <ul>
35
+ <li>
36
+ <input type="checkbox" name="minification-html"
37
+ value="1" <?php checked($basic['breeze-minify-html'], '1') ?>/>
38
+ <label class="breeze_tool_tip"><?php _e('HTML', 'breeze') ?></label>
39
+ </li>
40
+ <li>
41
+ <input type="checkbox" name="minification-css"
42
+ value="1" <?php checked($basic['breeze-minify-css'], '1') ?>/>
43
+ <label class="breeze_tool_tip"><?php _e('CSS', 'breeze') ?></label>
44
+ </li>
45
+ <li>
46
+ <input type="checkbox" name="minification-js"
47
+ value="1" <?php checked($basic['breeze-minify-js'], '1') ?>/>
48
+ <label class="breeze_tool_tip"><?php _e('JS', 'breeze') ?></label>
49
+ </li>
50
+ <li>
51
+ <label><?php _e('Use the above options to minify HTML, CSS, or JS files.', 'breeze') ?></label>
52
+ <br>
53
+ <label><b>Note:&nbsp;</b>
54
+ <span style="color: #ff0000"><?php _e('We recommend testing minification on a staging website before deploying it on a live website. Minification is known to cause issues on the frontend.', 'breeze') ?></span>
55
+ </label>
56
+ </li>
57
+ </ul>
58
+
59
+ </td>
60
+ </tr>
61
+ <tr>
62
+ <td>
63
+ <label for="gzip-compression"><?php _e('Gzip Compression', 'breeze') ?></label>
64
+ </td>
65
+ <td>
66
+ <input type="checkbox" id="gzip-compression" name="gzip-compression"
67
+ value='1' <?php checked($basic['breeze-gzip-compression'], '1') ?>/>
68
+ <label class="breeze_tool_tip"><?php _e('Enable this to compress your files making HTTP requests fewer and faster.', 'breeze') ?></label>
69
+ </td>
70
+ </tr>
71
+ <tr>
72
+ <td style="vertical-align: middle">
73
+ <label for="browser-cache"><?php _e('Browser Cache', 'breeze') ?></label>
74
+ </td>
75
+ <td>
76
+ <input type="checkbox" id="browser-cache" name="browser-cache"
77
+ value='1' <?php checked($basic['breeze-browser-cache'], '1') ?>/>
78
+ <label class="breeze_tool_tip"><?php _e('Enable this to add expires headers to static files. This will ask browsers to either request a file from server or fetch from the browser’s cache.', 'breeze') ?></label>
79
+ </td>
80
+ </tr>
81
+
82
+ <tr style="display: none;">
83
+ <td style="vertical-align: middle">
84
+ <label for="desktop-cache" class="breeze_tool_tip"> <?php _e('Desktop Cache', 'breeze') ?></label>
85
+ </td>
86
+ <td>
87
+ <select id="desktop-cache" name="desktop-cache">
88
+ <option value="1" <?php echo ($basic['breeze-desktop-cache'] == '1') ? 'selected="selected"' : '' ?>><?php _e('Activated', 'breeze') ?></option>
89
+ <option value="2" <?php echo ($basic['breeze-desktop-cache'] == '2') ? 'selected="selected"' : '' ?>><?php _e('No cache for desktop', 'breeze') ?></option>
90
+ </select>
91
+ </td>
92
+ </tr>
93
+
94
+ <tr style="display: none;">
95
+ <td style="vertical-align: middle">
96
+ <label for="mobile-cache" class="breeze_tool_tip"> <?php _e('Mobile Cache', 'breeze') ?></label>
97
+ </td>
98
+ <td>
99
+ <select id="mobile-cache" name="mobile-cache">
100
+ <option value="1" <?php echo ($basic['breeze-mobile-cache'] == '1') ? 'selected="selected"' : '' ?>><?php _e('Automatic (same as desktop)', 'breeze') ?></option>
101
+ <option value="2" <?php echo ($basic['breeze-mobile-cache'] == '2') ? 'selected="selected"' : '' ?>><?php _e('Specific mobile cache', 'breeze') ?></option>
102
+ <option value="3" <?php echo ($basic['breeze-mobile-cache'] == '3') ? 'selected="selected"' : '' ?>><?php _e('No cache for mobile', 'breeze') ?></option>
103
+ </select>
104
+ </td>
105
+ </tr>
106
+ </table>
views/tabs/cdn.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $cdn_integration = get_option('breeze_cdn_integration');
3
+
4
+ $cdn_content_value = '';
5
+ $cdn_exclude_content_value = '';
6
+ if(!empty($cdn_integration['cdn-content'])){
7
+ $cdn_content_value = implode(',',$cdn_integration['cdn-content']);
8
+ }
9
+ if(!empty($cdn_integration['cdn-exclude-content'])){
10
+ $cdn_exclude_content_value = implode(',',$cdn_integration['cdn-exclude-content']);
11
+ }
12
+ ?>
13
+ <table cellspacing="15">
14
+ <tr>
15
+ <td>
16
+ <label for="activate-cdn" class="breeze_tool_tip"><?php _e('Activate CDN', 'breeze')?></label>
17
+ </td>
18
+ <td>
19
+ <input type="checkbox" id="activate-cdn" name="activate-cdn" value="1" <?php checked($cdn_integration['cdn-active'],'1')?>/>
20
+ <label class="breeze_tool_tip"><?php _e('Enable to make CDN effective on your website.', 'breeze')?></label>
21
+ </td>
22
+ </tr>
23
+ <tr>
24
+ <td>
25
+ <label for="cdn-url" class="breeze_tool_tip"><?php _e('CDN Root URL', 'breeze')?></label>
26
+ </td>
27
+ <td>
28
+ <input type="text" id="cdn-url" name="cdn-url" size="50" placeholder="<?php _e('https://www.domain.com','breeze')?>" value="<?php echo (($cdn_integration['cdn-url'])?esc_html($cdn_integration['cdn-url']):''); ?>"/>
29
+ <label style="vertical-align: baseline" class="breeze_tool_tip"><?php _e('Enter the URL of CDN.', 'breeze')?></label>
30
+ <br>
31
+ <label class="breeze_tool_tip"><b>Note:&nbsp;</b><?php _e('Use double slash ‘//’ at the start or url, if you have some pages on HTTP and some are on HTTPS.', 'breeze') ?></label>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <td>
36
+ <label for="cdn-content" class="breeze_tool_tip" ><?php _e('CDN Content', 'breeze')?></label>
37
+ </td>
38
+ <td>
39
+ <input type="text" id="cdn-content" name="cdn-content" size="50" value="<?php echo (($cdn_content_value)?esc_html($cdn_content_value):''); ?>"/>
40
+ <br>
41
+ <label class="breeze_tool_tip"><b>Note:&nbsp;</b><?php _e('Enter the directories (comma separated) of which you want the CDN to serve the content.', 'breeze') ?></label>
42
+ </td>
43
+ </tr>
44
+ <tr>
45
+ <td>
46
+ <label for="cdn-exclude-content" class="breeze_tool_tip" ><?php _e('Exclude Content', 'breeze')?></label>
47
+ </td>
48
+ <td>
49
+ <input type="text" id="cdn-exclude-content" name="cdn-exclude-content" size="50" value="<?php echo (($cdn_exclude_content_value)?esc_html($cdn_exclude_content_value):''); ?>" />
50
+ <br>
51
+ <label class="breeze_tool_tip"><b>Note:&nbsp;</b><?php _e('Exclude file types or directories from CDN. Example, enter .css to exclude the CSS files.', 'breeze') ?></label>
52
+ </td>
53
+ </tr>
54
+ <tr>
55
+ <td>
56
+ <label for="cdn-relative-path" class="breeze_tool_tip" ><?php _e('Relative path', 'breeze')?></label>
57
+ </td>
58
+ <td>
59
+ <input type="checkbox" id="cdn-relative-path" name="cdn-relative-path" value="1" <?php checked($cdn_integration['cdn-relative-path'], '1')?>/>
60
+ <label class="breeze_tool_tip"><?php _e('Keep this option enabled. Use this option to enable relative path for your CDN on your WordPress site.', 'breeze')?></label>
61
+ </td>
62
+ </tr>
63
+ </table>
views/tabs/database.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="breeze-top-notice">
2
+ <label class="breeze_tool_tip"><?php _e('It is recommended to backup your Database before using the following options.','breeze')?></label>
3
+ </div>
4
+ <table cellspacing="15">
5
+ <tr>
6
+ <td>
7
+ <label for="data0" class="breeze_tool_tip"><?php _e('Select all','breeze')?></label>
8
+ </td>
9
+ <td>
10
+ <input type="checkbox" id="data0" name="all_control" value="all_data"/>
11
+ <label class="breeze_tool_tip"><?php _e('Select all following options. Click Optimize to perform actions.','breeze')?></label>
12
+ </td>
13
+ </tr>
14
+ <tr>
15
+ <td>
16
+ <label for="data1" class="breeze_tool_tip"><?php _e('Post revisions','breeze')?><?php echo "&nbsp(".(int)Breeze_Configuration::getElementToClean('revisions').")"; ?></label>
17
+ </td>
18
+ <td>
19
+ <input type="checkbox" id="data1" name="clean[]" class="clean-data" value="revisions"/>
20
+ <label class="breeze_tool_tip"><?php _e('Use this option to clear out post revisions from your WordPress database.','breeze')?></label>
21
+ </td>
22
+ </tr>
23
+ <tr>
24
+ <td>
25
+ <label for="data2" class="breeze_tool_tip" ><?php _e('Auto drafted content','breeze')?><?php echo "&nbsp(".(int)Breeze_Configuration::getElementToClean('drafted').")"; ?></label>
26
+ </td>
27
+ <td>
28
+ <input type="checkbox" id="data2" name="clean[]" class="clean-data" value="drafted"/>
29
+ <label class="breeze_tool_tip"><?php _e('Use this option to delete the auto saved drafts from WordPress database.','breeze')?></label>
30
+ </td>
31
+ </tr>
32
+ <tr>
33
+ <td>
34
+ <label for="data3" class="breeze_tool_tip" ><?php _e('All trashed content','breeze')?><?php echo "&nbsp(".(int)Breeze_Configuration::getElementToClean('trash').")"; ?></label>
35
+ </td>
36
+ <td>
37
+ <input type="checkbox" id="data3" name="clean[]" class="clean-data" value="trash"/>
38
+ <label class="breeze_tool_tip"><?php _e('Use this option to delete the trashed content from WordPress database.','breeze')?></label>
39
+
40
+ </td>
41
+ </tr>
42
+ <tr>
43
+ <td>
44
+ <label for="data4" class="breeze_tool_tip" ><?php _e('Comments from trash & spam','breeze')?><?php echo "&nbsp(".(int)Breeze_Configuration::getElementToClean('comments').")"; ?></label>
45
+ </td>
46
+ <td>
47
+ <input type="checkbox" id="data4" name="clean[]" class="clean-data" value="comments"/>
48
+ <label class="breeze_tool_tip"><?php _e('Use this option to cleanup trash and spam comments from WordPress database.','breeze')?></label>
49
+ </td>
50
+ </tr>
51
+ <tr>
52
+ <td>
53
+ <label for="data5" class="breeze_tool_tip" ><?php _e('Trackbacks and pingbacks','breeze')?><?php echo "&nbsp(".(int)Breeze_Configuration::getElementToClean('trackbacks').")"; ?></label>
54
+ </td>
55
+ <td>
56
+ <input type="checkbox" id="data5" name="clean[]" class="clean-data" value="trackbacks"/>
57
+ <label class="breeze_tool_tip"><?php _e('Cleanup Transients and Pingbacks from the WordPress database.','breeze')?></label>
58
+ </td>
59
+ </tr>
60
+ <tr>
61
+ <td>
62
+ <label for="data6" class="breeze_tool_tip" ><?php _e('Transient options','breeze')?><?php echo "&nbsp(".(int)Breeze_Configuration::getElementToClean('transient').")"; ?></label>
63
+ </td>
64
+ <td>
65
+ <input type="checkbox" id="data6" name="clean[]" class="clean-data" value="transient"/>
66
+ <label class="breeze_tool_tip"><?php _e('Cleanup expired and active Transients from WordPress.','breeze')?></label>
67
+ </td>
68
+ </tr>
69
+ </table>
views/tabs/varnish.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $varnish = get_option('breeze_varnish_cache');
3
+ $check_varnish = Breeze_Admin::check_varnish();
4
+ ?>
5
+ <div class="breeze-top-notice">
6
+ <label class="breeze_tool_tip"><?php _e('By default Varnish is enabled on all WordPress websites hosted on Cloudways.','breeze')?></label>
7
+ </div>
8
+ <table cellspacing="15">
9
+ <tr>
10
+ <td>
11
+ <label for="auto-purge-varnish" class="breeze_tool_tip"><?php _e('Auto Purge Varnish', 'breeze'); ?></label>
12
+ </td>
13
+ <td>
14
+ <input type="checkbox" id="auto-purge-varnish" name="auto-purge-varnish" value="1" <?php checked($varnish['auto-purge-varnish'], '1')?>/>
15
+ <label class="breeze_tool_tip" ><?php _e('Keep this option enabled to automatically purge Varnish cache on actions like publishing new blog posts, pages and comments.','breeze')?></label>
16
+ <br>
17
+ <?php if( !$check_varnish): ?>
18
+ <label><b>Note:&nbsp;</b>
19
+ <span style="color: #ff0000"><?php _e('Seems Varnish is disabled on your Application. Please refer to ', 'breeze') ?><a href="https://support.cloudways.com/most-common-varnish-issues-and-queries/" target="_blank">this KB</a><?php _e(' and learn how to enable it.','breeze') ?> </span>
20
+ </label>
21
+ <?php endif; ?>
22
+ </td>
23
+ </tr>
24
+ <tr>
25
+ <td>
26
+ <label for="varnish-server-ip" class="breeze_tool_tip"><?php _e('Varnish server', 'breeze'); ?></label>
27
+ </td>
28
+ <td>
29
+ <input type="text" id="varnish-server-ip" size="20" name="varnish-server-ip"
30
+ value='<?php echo(!empty($varnish['breeze-varnish-server-ip']) ? esc_html($varnish['breeze-varnish-server-ip']) : '127.0.0.1'); ?>'/>
31
+ <br/><span class="breeze_tool_tip" ><strong><?php _e('Note: Keep this default if you are a Cloudways customer. Otherwise ask your hosting provider on what to set here.','breeze')?></strong></span>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <td style="vertical-align: middle">
36
+ <label class="breeze_tool_tip"><?php _e('Purge Varnish Cache', 'breeze'); ?></label>
37
+ </td>
38
+ <td>
39
+ <input type="button" id="purge-varnish-button" class="button" value="<?php _e('Purge','breeze')?>" />
40
+ <label style="vertical-align: bottom"><?php _e('Use this option to instantly Purge Varnish Cache on entire website. ', 'breeze') ?></label>
41
+ </td>
42
+ </tr>
43
+ </table>