Custom CSS and Javascript - Version 2.0

Version Description

  • Added revisions
  • Added drafts/previewing
  • Added minification
Download this release

Release Info

Developer hearken
Plugin Icon 128x128 Custom CSS and Javascript
Version 2.0
Comparing to
See all releases

Code changes from version 1.0.6 to 2.0

css/custom-css-and-javascript.css ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Author: Hearken Media
3
+ * License: GNU General Public License version 2 or later
4
+ * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
5
+ */
6
+ #hm_custom_css_js_revisions {
7
+ margin-top: 5px;
8
+ }
9
+ #hm_custom_css_js_revisions li {
10
+ white-space: nowrap;
11
+ }
12
+ #hm_custom_css_js_revisions .active a.view-rev {
13
+ font-weight: bold;
14
+ }
custom-css-and-javascript.php CHANGED
@@ -2,9 +2,9 @@
2
  /**
3
  * Plugin Name: Custom CSS and Javascript
4
  * Description: Easily add custom CSS and Javascript code to your WordPress site.
5
- * Version: 1.0.6
6
- * Author: Hearken Media
7
- * Author URI: http://hearkenmedia.com/landing-wp-plugin.php?utm_source=custom-css-and-javascript&utm_medium=link&utm_campaign=wp-widget-link
8
  * License: GNU General Public License version 2 or later
9
  * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
10
  */
@@ -12,10 +12,15 @@
12
  add_action('wp_enqueue_scripts', 'hm_custom_css_js_scripts', 999999);
13
  function hm_custom_css_js_scripts() {
14
  $uploadDir = wp_upload_dir();
15
- if (file_exists($uploadDir['basedir'].'/hm_custom_css_js/custom.js'))
16
- wp_enqueue_script('hm_custom_js', $uploadDir['baseurl'].'/hm_custom_css_js/custom.js', array(), get_option('hm_custom_javascript_ver', 1));
17
- if (file_exists($uploadDir['basedir'].'/hm_custom_css_js/custom.css'))
18
- wp_enqueue_style('hm_custom_css', $uploadDir['baseurl'].'/hm_custom_css_js/custom.css', array(), get_option('hm_custom_css_ver', 1));
 
 
 
 
 
19
  }
20
  add_action('admin_menu', 'hm_custom_css_admin_menu');
21
  function hm_custom_css_admin_menu() {
@@ -35,7 +40,10 @@ function hm_custom_css_js_admin_scripts($hook) {
35
  else
36
  wp_enqueue_script('hm_custom_css_js_codemirror_mode_js', plugins_url('codemirror/mode/javascript.js', __FILE__));
37
  wp_enqueue_style('hm_custom_css_js_codemirror', plugins_url('codemirror/codemirror.css', __FILE__));
 
 
38
  }
 
39
  add_action('wp_ajax_hm_custom_css_js_save', 'hm_custom_css_js_save');
40
  function hm_custom_css_js_save() {
41
  if (!current_user_can('edit_theme_options') || empty($_POST['mode']) || !isset($_POST['code']))
@@ -43,15 +51,216 @@ function hm_custom_css_js_save() {
43
  $_POST['mode'] = strtolower($_POST['mode']);
44
  if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
45
  wp_send_json_error();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  $uploadDir = wp_upload_dir();
47
  if (!is_dir($uploadDir['basedir'].'/hm_custom_css_js'))
48
  mkdir($uploadDir['basedir'].'/hm_custom_css_js') or wp_send_json_error();
49
- if (file_put_contents($uploadDir['basedir'].'/hm_custom_css_js/custom.'.($_POST['mode'] == 'css' ? 'css' : 'js'), wp_unslash($_POST['code'])) === false)
 
50
  wp_send_json_error();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  update_option('hm_custom_'.$_POST['mode'].'_ver', time());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  wp_send_json_success();
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  function hm_custom_css_page() {
56
  hm_custom_css_js_page('CSS');
57
  }
@@ -68,69 +277,30 @@ function hm_custom_css_js_page($mode) {
68
  echo('
69
  <div class="wrap">
70
  <h2>Custom '.$mode.'</h2>
 
71
  <div>
72
- <div id="hm_custom_code_editor" style="margin-top: 15px;"></div>
 
 
 
 
 
 
 
73
  <div style="float: right; padding-left: 10px; margin-top: 3px; white-space: nowrap; font-style: italic;">
74
  <a href="https://codemirror.net/" target="_blank">CodeMirror</a> code editor
75
  </div>
76
  <button type="button" class="button-primary hm-custom-css-js-save-btn" style="margin-top: 15px;" disabled="disabled">Saved</button>
 
 
 
 
77
  </div>
78
- <script>
79
- var hm_custom_code_editor, hm_custom_code_editor_has_changes = false;
80
- jQuery(document).ready(function($) {
81
- hm_custom_code_editor = CodeMirror(document.getElementById("hm_custom_code_editor"), {
82
- lineNumbers: true,
83
- mode: "'.strtolower($mode).'",
84
- value: '.$code.'
85
- });
86
- hm_custom_code_editor.on("change", function() {
87
- if (hm_custom_code_editor_has_changes)
88
- return;
89
- hm_custom_code_editor_has_changes = true;
90
- $(".hm-custom-css-js-save-btn").html("Save").prop("disabled", false);
91
- });
92
- $(".hm-custom-css-js-save-btn").click(function() {
93
- $(".hm-custom-css-js-save-btn").prop("disabled", true).html("Saving...");
94
- $.post(ajaxurl, {action: "hm_custom_css_js_save", mode: "'.$mode.'", code: hm_custom_code_editor.getValue()})
95
- .done(function(data) {
96
- if (data.success) {
97
- $(".hm-custom-css-js-save-btn").html("Saved");
98
- hm_custom_code_editor_has_changes = false;
99
- } else {
100
- alert("Error while saving. Please try again.");
101
- $(".hm-custom-css-js-save-btn").html("Save").prop("disabled", false);
102
- }
103
- })
104
- .fail(function() {
105
- alert("Error while saving. Please try again.");
106
- $(".hm-custom-css-js-save-btn").html("Save").prop("disabled", false);
107
- });
108
- });
109
- $(window).resize(function() {
110
- $("#hm_custom_code_editor .CodeMirror").height(Math.max(150,
111
- $(window).height()
112
- - $("#hm_custom_code_editor").offset().top
113
- - $(".hm-custom-css-js-save-btn").height()
114
- - 30));
115
- hm_custom_code_editor.refresh();
116
- });
117
- $(window).resize();
118
- $(window).on("beforeunload", function(ev) {
119
- if (hm_custom_code_editor_has_changes) {
120
- ev.returnValue = "You have unsaved changes that will be lost if you leave this page!";
121
- return ev.returnValue;
122
- }
123
- });
124
- });
125
- </script>
126
-
127
- <div style="background-color: #fff; border: 1px solid #ccc; padding: 20px; display: inline-block; margin-top: 30px;">
128
- <h3 style="margin: 0;">Plugin by:</h3>
129
- <a href="http://hearkenmedia.com/landing-wp-plugin.php?utm_source=custom-css-and-javascript&utm_medium=link&utm_campaign=wp-widget-link" target="_blank"><img src="'.plugins_url('images/hm-logo.png', __FILE__).'" alt="Hearken Media" style="width: 250px;" /></a><br />
130
- <a href="https://wordpress.org/support/view/plugin-reviews/custom-css-and-javascript" target="_blank"><strong>
131
- If you find this plugin useful, please write a brief review!
132
- </strong></a>
133
- </div>
134
  </div>
135
  ');
136
  }
2
  /**
3
  * Plugin Name: Custom CSS and Javascript
4
  * Description: Easily add custom CSS and Javascript code to your WordPress site.
5
+ * Version: 2.0
6
+ * Author: Potent Plugins
7
+ * Author URI: http://potentplugins.com/?utm_source=custom-css-and-javascript&utm_medium=link&utm_campaign=wp-plugin-credit-link
8
  * License: GNU General Public License version 2 or later
9
  * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
10
  */
12
  add_action('wp_enqueue_scripts', 'hm_custom_css_js_scripts', 999999);
13
  function hm_custom_css_js_scripts() {
14
  $uploadDir = wp_upload_dir();
15
+ if (current_user_can('edit_theme_options')) {
16
+ wp_enqueue_script('hm_custom_js', get_site_url(null, '/index.php').'?hm_custom_js_draft=1', array(), time());
17
+ wp_enqueue_style('hm_custom_css', get_site_url(null, '/index.php').'?hm_custom_css_draft=1', array(), time());
18
+ } else {
19
+ if (file_exists($uploadDir['basedir'].'/hm_custom_css_js/custom.js'))
20
+ wp_enqueue_script('hm_custom_js', $uploadDir['baseurl'].'/hm_custom_css_js/custom.js', array(), get_option('hm_custom_javascript_ver', 1));
21
+ if (file_exists($uploadDir['basedir'].'/hm_custom_css_js/custom.css'))
22
+ wp_enqueue_style('hm_custom_css', $uploadDir['baseurl'].'/hm_custom_css_js/custom.css', array(), get_option('hm_custom_css_ver', 1));
23
+ }
24
  }
25
  add_action('admin_menu', 'hm_custom_css_admin_menu');
26
  function hm_custom_css_admin_menu() {
40
  else
41
  wp_enqueue_script('hm_custom_css_js_codemirror_mode_js', plugins_url('codemirror/mode/javascript.js', __FILE__));
42
  wp_enqueue_style('hm_custom_css_js_codemirror', plugins_url('codemirror/codemirror.css', __FILE__));
43
+ wp_enqueue_script('hm_custom_css_js', plugins_url('js/custom-css-and-javascript.js', __FILE__));
44
+ wp_enqueue_style('hm_custom_css_js', plugins_url('css/custom-css-and-javascript.css', __FILE__));
45
  }
46
+
47
  add_action('wp_ajax_hm_custom_css_js_save', 'hm_custom_css_js_save');
48
  function hm_custom_css_js_save() {
49
  if (!current_user_can('edit_theme_options') || empty($_POST['mode']) || !isset($_POST['code']))
51
  $_POST['mode'] = strtolower($_POST['mode']);
52
  if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
53
  wp_send_json_error();
54
+
55
+ $_POST['code'] = wp_unslash($_POST['code']);
56
+
57
+ $rev_id = wp_insert_post(array(
58
+ 'post_content' => $_POST['code'],
59
+ 'post_status' => 'draft',
60
+ 'post_type' => 'hm_custom_'.$_POST['mode'],
61
+ ));
62
+ if ($rev_id === false)
63
+ wp_send_json_error();
64
+
65
+ wp_send_json_success($rev_id);
66
+ }
67
+
68
+ add_action('wp_ajax_hm_custom_css_js_publish', 'hm_custom_css_js_publish');
69
+ function hm_custom_css_js_publish() {
70
+ if (!current_user_can('edit_theme_options') || empty($_POST['mode']) || !isset($_POST['rev']) || !is_numeric($_POST['rev']))
71
+ wp_send_json_error();
72
+ $_POST['mode'] = strtolower($_POST['mode']);
73
+ if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
74
+ wp_send_json_error();
75
+
76
+ $post = get_post($_POST['rev']);
77
+ if ($post->post_type != 'hm_custom_'.$_POST['mode'])
78
+ wp_send_json_error();
79
+
80
  $uploadDir = wp_upload_dir();
81
  if (!is_dir($uploadDir['basedir'].'/hm_custom_css_js'))
82
  mkdir($uploadDir['basedir'].'/hm_custom_css_js') or wp_send_json_error();
83
+ $outputFile = $uploadDir['basedir'].'/hm_custom_css_js/custom.'.($_POST['mode'] == 'css' ? 'css' : 'js');
84
+ if (file_put_contents($outputFile, $post->post_content) === false)
85
  wp_send_json_error();
86
+ if (empty($_POST['minify'])) {
87
+ update_option('hm_custom_'.$_POST['mode'].'_minify', false);
88
+ } else {
89
+ update_option('hm_custom_'.$_POST['mode'].'_minify', true);
90
+ require_once(__DIR__.'/minify/src/Minify.php');
91
+ require_once(__DIR__.'/minify/src/Exception.php');
92
+ if ($_POST['mode'] == 'css') {
93
+ require_once(__DIR__.'/minify/src/CSS.php');
94
+ require_once(__DIR__.'/minify/src/Converter.php');
95
+ $minifier = new MatthiasMullie\Minify\CSS;
96
+ } else {
97
+ require_once(__DIR__.'/minify/src/JS.php');
98
+ $minifier = new MatthiasMullie\Minify\JS;
99
+ }
100
+ $minifier->add($outputFile);
101
+ $minifier->minify($outputFile);
102
+ }
103
+
104
  update_option('hm_custom_'.$_POST['mode'].'_ver', time());
105
+
106
+ // Unpublish previous revisions
107
+ $wp_query = new WP_Query(array(
108
+ 'post_type' => 'hm_custom_'.$_POST['mode'],
109
+ 'post_status' => 'publish',
110
+ 'fields' => 'ids',
111
+ 'nopaging' => true
112
+ ));
113
+ $posts = $wp_query->get_posts();
114
+ foreach ($posts as $postId) {
115
+ if (!wp_update_post(array(
116
+ 'ID' => $postId,
117
+ 'post_status' => 'draft',
118
+ )))
119
+ wp_send_json_error();
120
+ }
121
+
122
+ if (!wp_update_post(array(
123
+ 'ID' => $_POST['rev'],
124
+ 'post_status' => 'publish',
125
+ 'post_date' => current_time('Y-m-d H:i:s'),
126
+ )))
127
+ wp_send_json_error();
128
+
129
  wp_send_json_success();
130
  }
131
 
132
+ add_action('wp_ajax_hm_custom_css_js_delete_revision', 'hm_custom_css_js_delete_revision');
133
+ function hm_custom_css_js_delete_revision() {
134
+ if (!current_user_can('edit_theme_options') || empty($_POST['mode']) || !isset($_POST['rev']) || !is_numeric($_POST['rev']))
135
+ wp_send_json_error();
136
+ $_POST['mode'] = strtolower($_POST['mode']);
137
+ if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
138
+ wp_send_json_error();
139
+
140
+ $post = get_post($_POST['rev']);
141
+ if ($post->post_type != 'hm_custom_'.$_POST['mode'] || $post->post_status == 'publish')
142
+ wp_send_json_error();
143
+
144
+
145
+ if (!wp_delete_post($post->ID, true))
146
+ wp_send_json_error();
147
+
148
+ wp_send_json_success();
149
+ }
150
+
151
+ add_action('wp_ajax_hm_custom_css_js_delete_revisions', 'hm_custom_css_js_delete_revisions');
152
+ function hm_custom_css_js_delete_revisions() {
153
+ if (!current_user_can('edit_theme_options') || empty($_POST['mode']))
154
+ wp_send_json_error();
155
+ $_POST['mode'] = strtolower($_POST['mode']);
156
+ if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
157
+ wp_send_json_error();
158
+
159
+ $wp_query = new WP_Query(array(
160
+ 'post_type' => 'hm_custom_'.$_POST['mode'],
161
+ 'post_status' => 'draft',
162
+ 'fields' => 'ids',
163
+ 'nopaging' => true
164
+ ));
165
+ $posts = $wp_query->get_posts();
166
+ foreach ($posts as $postId) {
167
+ if (!wp_delete_post($postId, true))
168
+ wp_send_json_error();
169
+ }
170
+
171
+ wp_send_json_success();
172
+ }
173
+
174
+
175
+ add_action('wp_ajax_hm_custom_css_js_get_revisions', 'hm_custom_css_js_get_revisions');
176
+ function hm_custom_css_js_get_revisions() {
177
+ if (!current_user_can('edit_theme_options') || empty($_POST['mode']))
178
+ wp_send_json_error();
179
+ $_POST['mode'] = strtolower($_POST['mode']);
180
+ if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
181
+ wp_send_json_error();
182
+
183
+ $wp_query = new WP_Query();
184
+ $posts = $wp_query->query(array(
185
+ 'post_type' => 'hm_custom_'.$_POST['mode'],
186
+ 'post_status' => 'any',
187
+ 'nopaging' => true
188
+ ));
189
+
190
+
191
+ $revisions = array();
192
+ if (empty($posts)) {
193
+ $uploadDir = wp_upload_dir();
194
+ $customFile = $uploadDir['basedir'].'/hm_custom_css_js/custom.'.($_POST['mode'] == 'css' ? 'css' : 'js');
195
+ if (file_exists($customFile)) {
196
+ $contents = file_get_contents($customFile);
197
+ if ($contents === false)
198
+ wp_send_json_error();
199
+ $rev_id = wp_insert_post(array(
200
+ 'post_content' => $contents,
201
+ 'post_status' => 'publish',
202
+ 'post_type' => 'hm_custom_'.$_POST['mode'],
203
+ ));
204
+ $revisions[] = array('id' => $rev_id, 'rev_date' => current_time('Y-m-d H:i:s'), 'published' => true);
205
+ }
206
+ } else {
207
+ foreach ($posts as $post) {
208
+ $revisions[] = array('id' => $post->ID, 'rev_date' => $post->post_date, 'published' => ($post->post_status == 'publish'));
209
+ }
210
+ }
211
+
212
+ wp_send_json_success($revisions);
213
+ }
214
+
215
+ add_action('wp_ajax_hm_custom_css_js_get_revision', 'hm_custom_css_js_get_revision');
216
+ function hm_custom_css_js_get_revision() {
217
+ if (!current_user_can('edit_theme_options') || empty($_POST['mode']) || !isset($_POST['rev']) || !is_numeric($_POST['rev']))
218
+ wp_send_json_error();
219
+ $_POST['mode'] = strtolower($_POST['mode']);
220
+ if ($_POST['mode'] != 'css' && $_POST['mode'] != 'javascript')
221
+ wp_send_json_error();
222
+
223
+ $post = get_post($_POST['rev']);
224
+ if ($post->post_type != 'hm_custom_'.$_POST['mode'])
225
+ wp_send_json_error();
226
+
227
+ wp_send_json_success(array(
228
+ 'id' => $post->ID,
229
+ 'content' => $post->post_content
230
+ ));
231
+ }
232
+
233
+ add_action('init', 'hm_custom_css_js_init');
234
+ function hm_custom_css_js_init() {
235
+ register_post_type('hm_custom_css');
236
+ register_post_type('hm_custom_javascript');
237
+
238
+ if (!empty($_GET['hm_custom_css_draft'])) {
239
+ $wp_query = new WP_Query(array(
240
+ 'post_type' => 'hm_custom_css',
241
+ 'post_status' => 'any',
242
+ 'posts_per_page' => 1
243
+ ));
244
+ $posts = $wp_query->get_posts();
245
+ header('Content-Type: text/css');
246
+ if (isset($posts[0]))
247
+ echo($posts[0]->post_content);
248
+ exit;
249
+ }
250
+ if (!empty($_GET['hm_custom_js_draft'])) {
251
+ $wp_query = new WP_Query(array(
252
+ 'post_type' => 'hm_custom_javascript',
253
+ 'post_status' => 'any',
254
+ 'posts_per_page' => 1
255
+ ));
256
+ $posts = $wp_query->get_posts();
257
+ header('Content-Type: text/javascript');
258
+ if (isset($posts[0]))
259
+ echo($posts[0]->post_content);
260
+ exit;
261
+ }
262
+ }
263
+
264
  function hm_custom_css_page() {
265
  hm_custom_css_js_page('CSS');
266
  }
277
  echo('
278
  <div class="wrap">
279
  <h2>Custom '.$mode.'</h2>
280
+ <script>var hm_custom_css_js_mode = "'.$mode.'";</script>
281
  <div>
282
+ <div id="hm_custom_code_editor" style="margin-top: 15px;">
283
+ <div style="width: 200px; height: 100%; overflow: auto; float: right; padding: 0 20px;">
284
+ <h4 style="margin: 0; margin-bottom: 5px;">Revisions:</h4>
285
+ <button class="button-secondary hm-custom-css-js-delete-revisions-btn">Delete All</button>
286
+ <ul id="hm_custom_css_js_revisions">
287
+ </ul>
288
+ </div>
289
+ </div>
290
  <div style="float: right; padding-left: 10px; margin-top: 3px; white-space: nowrap; font-style: italic;">
291
  <a href="https://codemirror.net/" target="_blank">CodeMirror</a> code editor
292
  </div>
293
  <button type="button" class="button-primary hm-custom-css-js-save-btn" style="margin-top: 15px;" disabled="disabled">Saved</button>
294
+ <button type="button" class="button-primary hm-custom-css-js-publish-btn" style="margin-top: 15px; margin-right: 10px;" disabled="disabled">Save &amp; Publish</button>
295
+ <label style="margin-top: 15px; white-space: nowrap;">
296
+ <input type="checkbox" class="hm-custom-css-js-minify-cb"'.(get_option('hm_custom_'.strtolower($mode).'_minify', true) ? ' checked="checked"' : '').' /> Minify output
297
+ </label>
298
  </div>
299
+ <div style="clear: both; margin-bottom: 20px;"></div>
300
+ ');
301
+ $potent_slug = 'custom-css-and-javascript';
302
+ include(__DIR__.'/plugin-credit.php');
303
+ echo('
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  </div>
305
  ');
306
  }
images/potent-logo.png ADDED
Binary file
js/custom-css-and-javascript.js ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Author: Hearken Media
3
+ * License: GNU General Public License version 2 or later
4
+ * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
5
+ */
6
+ var hm_custom_code_editor, hm_custom_code_editor_has_changes = false, hm_custom_css_js_save_publish = false, hm_custom_css_js_rev = 0, hm_custom_css_js_published_rev = 0;
7
+ jQuery(document).ready(function($) {
8
+ hm_custom_code_editor = CodeMirror(document.getElementById("hm_custom_code_editor"), {
9
+ lineNumbers: true,
10
+ mode: hm_custom_css_js_mode.toLowerCase()
11
+ });
12
+ hm_custom_code_editor.on("change", function() {
13
+ if (hm_custom_code_editor_has_changes)
14
+ return;
15
+ hm_custom_code_editor_has_changes = true;
16
+ $(".hm-custom-css-js-save-btn").html("Save").prop("disabled", false);
17
+ $(".hm-custom-css-js-publish-btn").html("Save &amp; Publish").prop("disabled", false);
18
+ });
19
+ $(".hm-custom-css-js-save-btn").click(function() {
20
+ $(".hm-custom-css-js-save-btn").prop("disabled", true).html("Saving...");
21
+ $.post(ajaxurl, {action: "hm_custom_css_js_save", mode: hm_custom_css_js_mode, code: hm_custom_code_editor.getValue()})
22
+ .done(function(data) {
23
+ if (data.success) {
24
+ $(".hm-custom-css-js-save-btn").html("Saved");
25
+ hm_custom_css_js_rev = data.data;
26
+ hm_custom_code_editor_has_changes = false;
27
+ if (hm_custom_css_js_save_publish)
28
+ $(".hm-custom-css-js-publish-btn").click()
29
+ else
30
+ hm_custom_css_js_get_revisions();
31
+ } else {
32
+ alert("Error while saving. Please try again.");
33
+ $(".hm-custom-css-js-save-btn").html("Save").prop("disabled", false);
34
+ if (hm_custom_css_js_save_publish)
35
+ $(".hm-custom-css-js-publish-btn").html("Save &amp; Publish").prop("disabled", true);
36
+ }
37
+ })
38
+ .fail(function() {
39
+ alert("Error while saving. Please try again.");
40
+ $(".hm-custom-css-js-save-btn").html("Save").prop("disabled", false);
41
+ if (hm_custom_css_js_save_publish)
42
+ $(".hm-custom-css-js-publish-btn").html("Save &amp; Publish").prop("disabled", true);
43
+ });
44
+ });
45
+ $(".hm-custom-css-js-publish-btn").click(function() {
46
+ $(".hm-custom-css-js-publish-btn").prop("disabled", true).html("Publishing...");
47
+ if (!$(".hm-custom-css-js-save-btn").prop("disabled")) {
48
+ hm_custom_css_js_save_publish = true;
49
+ $(".hm-custom-css-js-save-btn").click();
50
+ return;
51
+ }
52
+ hm_custom_css_js_save_publish = false;
53
+
54
+ $.post(ajaxurl, {action: "hm_custom_css_js_publish", mode: hm_custom_css_js_mode, minify: ($('.hm-custom-css-js-minify-cb').prop('checked') ? 1 : 0), rev: hm_custom_css_js_rev})
55
+ .done(function(data) {
56
+ if (data.success) {
57
+ $(".hm-custom-css-js-publish-btn").html("Published");
58
+ hm_custom_css_js_get_revisions();
59
+ } else {
60
+ alert("Error while publishing. Please try again.");
61
+ $(".hm-custom-css-js-publish-btn").html("Save &amp; Publish").prop("disabled", false);
62
+ }
63
+ })
64
+ .fail(function() {
65
+ alert("Error while publishing. Please try again.");
66
+ $(".hm-custom-css-js-publish-btn").html("Save &amp; Publish").prop("disabled", false);
67
+ });
68
+ });
69
+
70
+ $(".hm-custom-css-js-delete-revisions-btn").click(function() {
71
+ $(this).prop('disabled', true).html('Deleting...');
72
+
73
+ $.post(ajaxurl, {action: "hm_custom_css_js_delete_revisions", mode: hm_custom_css_js_mode})
74
+ .done(function(data) {
75
+ if (data.success) {
76
+ hm_custom_css_js_get_revisions();
77
+ $(".hm-custom-css-js-delete-revisions-btn").html('Delete All').prop('disabled', false);
78
+ } else {
79
+ alert("Error while deleting. Please try again.");
80
+ $(".hm-custom-css-js-delete-revisions-btn").html('Delete All').prop('disabled', false);
81
+ }
82
+ })
83
+ .fail(function() {
84
+ alert("Error while deleting. Please try again.");
85
+ $(".hm-custom-css-js-delete-revisions-btn").html('Delete All').prop('disabled', false);
86
+ });
87
+ });
88
+
89
+
90
+ $(window).resize(function() {
91
+ $("#hm_custom_code_editor, #hm_custom_code_editor .CodeMirror").height(Math.max(150,
92
+ $(window).height()
93
+ - $("#hm_custom_code_editor").offset().top
94
+ - $(".hm-custom-css-js-save-btn").height()
95
+ - 30));
96
+ hm_custom_code_editor.refresh();
97
+ });
98
+ $(window).resize();
99
+ $(window).on("beforeunload", function(ev) {
100
+ if (hm_custom_code_editor_has_changes) {
101
+ ev.returnValue = "You have unsaved changes that will be lost if you leave this page!";
102
+ return ev.returnValue;
103
+ }
104
+ });
105
+
106
+ $("#hm_custom_css_js_revisions").on("click", "li > a.view-rev", function(ev) {
107
+
108
+ if (hm_custom_code_editor_has_changes &&
109
+ !confirm("You have unsaved changes that will be lost if you view this revision!"))
110
+ return;
111
+
112
+ var revId = $(this).parent().attr("id").substring(20);
113
+
114
+ $.post(ajaxurl, {action: "hm_custom_css_js_get_revision", mode: hm_custom_css_js_mode, rev: revId})
115
+ .done(function(data) {
116
+ if (data.success) {
117
+ hm_custom_code_editor.doc.setValue(data.data.content);
118
+ hm_custom_css_js_rev = data.data.id;
119
+ $('#hm_custom_css_js_revisions .active').removeClass('active');
120
+ $('#hm_custom_css_js_rev' + hm_custom_css_js_rev).addClass('active');
121
+ $(".hm-custom-css-js-save-btn").html("Saved").prop("disabled", true);
122
+ if (hm_custom_css_js_rev == hm_custom_css_js_published_rev)
123
+ $(".hm-custom-css-js-publish-btn").html("Published").prop("disabled", true);
124
+ hm_custom_code_editor_has_changes = false;
125
+ } else {
126
+ alert("Error while loading. Please try again.");
127
+ }
128
+ })
129
+ .fail(function() {
130
+ alert("Error while loading. Please try again.");
131
+ });
132
+ });
133
+
134
+ $("#hm_custom_css_js_revisions").on("click", "li > a.del-rev", function(ev) {
135
+
136
+ var revId = $(this).parent().attr("id").substring(20);
137
+
138
+ $.post(ajaxurl, {action: "hm_custom_css_js_delete_revision", mode: hm_custom_css_js_mode, rev: revId})
139
+ .done(function(data) {
140
+ if (data.success) {
141
+ hm_custom_css_js_get_revisions();
142
+ } else {
143
+ alert("Error while deleting. Please try again.");
144
+ }
145
+ })
146
+ .fail(function() {
147
+ alert("Error while deleting. Please try again.");
148
+ });
149
+ });
150
+
151
+ function hm_custom_css_js_get_revisions() {
152
+ $.post(ajaxurl, {action: "hm_custom_css_js_get_revisions", mode: hm_custom_css_js_mode, })
153
+ .done(function(data) {
154
+ if (data.success) {
155
+ $("#hm_custom_css_js_revisions").empty();
156
+ if (data.data.length == 0) {
157
+ $("#hm_custom_css_js_revisions").append("<li>None</li>");
158
+ } else {
159
+ for (var i = 0; i < data.data.length; ++i) {
160
+ $("#hm_custom_css_js_revisions").append("<li id=\"hm_custom_css_js_rev" + data.data[i].id + "\"><a class=\"view-rev\" href=\"javascript:void(0);\">" + data.data[i].rev_date + "</a>" + (data.data[i].published ? " [published]" : " <a class=\"del-rev\" href=\"javascript:void(0);\">[delete]</a>") + "</li>");
161
+ if (data.data[i].published)
162
+ hm_custom_css_js_published_rev = data.data[i].id;
163
+ }
164
+ if (hm_custom_css_js_rev == 0) {
165
+ $("#hm_custom_css_js_revisions > li:first-child > a.view-rev").click();
166
+ } else {
167
+ $('#hm_custom_css_js_rev' + hm_custom_css_js_rev).addClass('active');
168
+ }
169
+ }
170
+ }
171
+ });
172
+ }
173
+ hm_custom_css_js_get_revisions();
174
+
175
+ });
minify/LICENSE ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2012 Matthias Mullie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
minify/data/js/keywords_after.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ in
2
+ public
3
+ extends
4
+ private
5
+ protected
6
+ implements
7
+ instanceof
minify/data/js/keywords_before.txt ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ do
2
+ in
3
+ let
4
+ new
5
+ var
6
+ case
7
+ else
8
+ enum
9
+ void
10
+ with
11
+ class
12
+ const
13
+ yield
14
+ delete
15
+ export
16
+ import
17
+ public
18
+ static
19
+ typeof
20
+ extends
21
+ package
22
+ private
23
+ continue
24
+ function
25
+ protected
26
+ implements
27
+ instanceof
minify/data/js/keywords_reserved.txt ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ do
2
+ if
3
+ in
4
+ for
5
+ let
6
+ new
7
+ try
8
+ var
9
+ case
10
+ else
11
+ enum
12
+ eval
13
+ null
14
+ this
15
+ true
16
+ void
17
+ with
18
+ break
19
+ catch
20
+ class
21
+ const
22
+ false
23
+ super
24
+ throw
25
+ while
26
+ yield
27
+ delete
28
+ export
29
+ import
30
+ public
31
+ return
32
+ static
33
+ switch
34
+ typeof
35
+ default
36
+ extends
37
+ finally
38
+ package
39
+ private
40
+ continue
41
+ debugger
42
+ function
43
+ arguments
44
+ interface
45
+ protected
46
+ implements
47
+ instanceof
minify/data/js/operators_after.txt ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ .
36
+ [
37
+ ]
38
+ ?
39
+ :
40
+ ,
41
+ ;
42
+ (
43
+ )
44
+ {
45
+ }
minify/data/js/operators_before.txt ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ +
2
+ -
3
+ *
4
+ /
5
+ %
6
+ =
7
+ +=
8
+ -=
9
+ *=
10
+ /=
11
+ %=
12
+ <<=
13
+ >>=
14
+ >>>=
15
+ &=
16
+ ^=
17
+ |=
18
+ &
19
+ |
20
+ ^
21
+ ~
22
+ <<
23
+ >>
24
+ >>>
25
+ ==
26
+ ===
27
+ !=
28
+ !==
29
+ >
30
+ <
31
+ >=
32
+ <=
33
+ &&
34
+ ||
35
+ !
36
+ .
37
+ [
38
+ ?
39
+ :
40
+ ,
41
+ ;
42
+ (
43
+ {
minify/src/CSS.php ADDED
@@ -0,0 +1,573 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace MatthiasMullie\Minify;
4
+
5
+ use MatthiasMullie\PathConverter\Converter;
6
+
7
+ /**
8
+ * CSS minifier.
9
+ *
10
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
11
+ *
12
+ * @author Matthias Mullie <minify@mullie.eu>
13
+ * @author Tijs Verkoyen <minify@verkoyen.eu>
14
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
15
+ * @license MIT License
16
+ */
17
+ class CSS extends Minify
18
+ {
19
+ /**
20
+ * @var int
21
+ */
22
+ protected $maxImportSize = 5;
23
+
24
+ /**
25
+ * @var string[]
26
+ */
27
+ protected $importExtensions = array(
28
+ 'gif' => 'data:image/gif',
29
+ 'png' => 'data:image/png',
30
+ 'jpe' => 'data:image/jpeg',
31
+ 'jpg' => 'data:image/jpeg',
32
+ 'jpeg' => 'data:image/jpeg',
33
+ 'svg' => 'data:image/svg+xml',
34
+ 'woff' => 'data:application/x-font-woff',
35
+ 'tif' => 'image/tiff',
36
+ 'tiff' => 'image/tiff',
37
+ 'xbm' => 'image/x-xbitmap',
38
+ );
39
+
40
+ /**
41
+ * Set the maximum size if files to be imported.
42
+ *
43
+ * Files larger than this size (in kB) will not be imported into the CSS.
44
+ * Importing files into the CSS as data-uri will save you some connections,
45
+ * but we should only import relatively small decorative images so that our
46
+ * CSS file doesn't get too bulky.
47
+ *
48
+ * @param int $size Size in kB
49
+ */
50
+ public function setMaxImportSize($size)
51
+ {
52
+ $this->maxImportSize = $size;
53
+ }
54
+
55
+ /**
56
+ * Set the type of extensions to be imported into the CSS (to save network
57
+ * connections).
58
+ * Keys of the array should be the file extensions & respective values
59
+ * should be the data type.
60
+ *
61
+ * @param string[] $extensions Array of file extensions
62
+ */
63
+ public function setImportExtensions(array $extensions)
64
+ {
65
+ $this->importExtensions = $extensions;
66
+ }
67
+
68
+ /**
69
+ * Move any import statements to the top.
70
+ *
71
+ * @param $content string Nearly finished CSS content
72
+ *
73
+ * @return string
74
+ */
75
+ protected function moveImportsToTop($content)
76
+ {
77
+ if (preg_match_all('/@import[^;]+;/', $content, $matches)) {
78
+
79
+ // remove from content
80
+ foreach ($matches[0] as $import) {
81
+ $content = str_replace($import, '', $content);
82
+ }
83
+
84
+ // add to top
85
+ $content = implode('', $matches[0]).$content;
86
+ };
87
+
88
+ return $content;
89
+ }
90
+
91
+ /**
92
+ * Combine CSS from import statements.
93
+ *
94
+ * @import's will be loaded and their content merged into the original file,
95
+ * to save HTTP requests.
96
+ *
97
+ * @param string $source The file to combine imports for.
98
+ * @param string $content The CSS content to combine imports for.
99
+ *
100
+ * @return string
101
+ */
102
+ protected function combineImports($source, $content)
103
+ {
104
+ $importRegexes = array(
105
+ // @import url(xxx)
106
+ '/
107
+ # import statement
108
+ @import
109
+
110
+ # whitespace
111
+ \s+
112
+
113
+ # open url()
114
+ url\(
115
+
116
+ # (optional) open path enclosure
117
+ (?P<quotes>["\']?)
118
+
119
+ # fetch path
120
+ (?P<path>
121
+
122
+ # do not fetch data uris or external sources
123
+ (?!(
124
+ ["\']?
125
+ (data|https?):
126
+ ))
127
+
128
+ .+?
129
+ )
130
+
131
+ # (optional) close path enclosure
132
+ (?P=quotes)
133
+
134
+ # close url()
135
+ \)
136
+
137
+ # (optional) trailing whitespace
138
+ \s*
139
+
140
+ # (optional) media statement(s)
141
+ (?P<media>[^;]*)
142
+
143
+ # (optional) trailing whitespace
144
+ \s*
145
+
146
+ # (optional) closing semi-colon
147
+ ;?
148
+
149
+ /ix',
150
+
151
+ // @import 'xxx'
152
+ '/
153
+
154
+ # import statement
155
+ @import
156
+
157
+ # whitespace
158
+ \s+
159
+
160
+ # open path enclosure
161
+ (?P<quotes>["\'])
162
+
163
+ # fetch path
164
+ (?P<path>
165
+
166
+ # do not fetch data uris or external sources
167
+ (?!(
168
+ ["\']?
169
+ (data|https?):
170
+ ))
171
+
172
+ .+?
173
+ )
174
+
175
+ # close path enclosure
176
+ (?P=quotes)
177
+
178
+ # (optional) trailing whitespace
179
+ \s*
180
+
181
+ # (optional) media statement(s)
182
+ (?P<media>[^;]*)
183
+
184
+ # (optional) trailing whitespace
185
+ \s*
186
+
187
+ # (optional) closing semi-colon
188
+ ;?
189
+
190
+ /ix',
191
+ );
192
+
193
+ // find all relative imports in css
194
+ $matches = array();
195
+ foreach ($importRegexes as $importRegex) {
196
+ if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) {
197
+ $matches = array_merge($matches, $regexMatches);
198
+ }
199
+ }
200
+
201
+ $search = array();
202
+ $replace = array();
203
+
204
+ // loop the matches
205
+ foreach ($matches as $match) {
206
+ // get the path for the file that will be imported
207
+ $importPath = dirname($source).'/'.$match['path'];
208
+
209
+ // only replace the import with the content if we can grab the
210
+ // content of the file
211
+ if (file_exists($importPath) && is_file($importPath)) {
212
+ // grab referenced file & minify it (which may include importing
213
+ // yet other @import statements recursively)
214
+ $minifier = new static($importPath);
215
+ $importContent = $minifier->execute($source);
216
+
217
+ // check if this is only valid for certain media
218
+ if ($match['media']) {
219
+ $importContent = '@media '.$match['media'].'{'.$importContent.'}';
220
+ }
221
+
222
+ // add to replacement array
223
+ $search[] = $match[0];
224
+ $replace[] = $importContent;
225
+ }
226
+ }
227
+
228
+ // replace the import statements
229
+ $content = str_replace($search, $replace, $content);
230
+
231
+ return $content;
232
+ }
233
+
234
+ /**
235
+ * Import files into the CSS, base64-ized.
236
+ *
237
+ * @url(image.jpg) images will be loaded and their content merged into the
238
+ * original file, to save HTTP requests.
239
+ *
240
+ * @param string $source The file to import files for.
241
+ * @param string $content The CSS content to import files for.
242
+ *
243
+ * @return string
244
+ */
245
+ protected function importFiles($source, $content)
246
+ {
247
+ $extensions = array_keys($this->importExtensions);
248
+ $regex = '/url\((["\']?)((?!["\']?data:).*?\.('.implode('|', $extensions).'))\\1\)/i';
249
+ if ($extensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
250
+ $search = array();
251
+ $replace = array();
252
+
253
+ // loop the matches
254
+ foreach ($matches as $match) {
255
+ // get the path for the file that will be imported
256
+ $path = $match[2];
257
+ $path = dirname($source).'/'.$path;
258
+ $extension = $match[3];
259
+
260
+ // only replace the import with the content if we're able to get
261
+ // the content of the file, and it's relatively small
262
+ $import = file_exists($path);
263
+ $import = $import && is_file($path);
264
+ $import = $import && filesize($path) <= $this->maxImportSize * 1024;
265
+ if (!$import) {
266
+ continue;
267
+ }
268
+
269
+ // grab content && base64-ize
270
+ $importContent = $this->load($path);
271
+ $importContent = base64_encode($importContent);
272
+
273
+ // build replacement
274
+ $search[] = $match[0];
275
+ $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
276
+ }
277
+
278
+ // replace the import statements
279
+ $content = str_replace($search, $replace, $content);
280
+ }
281
+
282
+ return $content;
283
+ }
284
+
285
+ /**
286
+ * Minify the data.
287
+ * Perform CSS optimizations.
288
+ *
289
+ * @param string[optional] $path Path to write the data to.
290
+ *
291
+ * @return string The minified data.
292
+ */
293
+ public function execute($path = null)
294
+ {
295
+ $content = '';
296
+
297
+ // loop files
298
+ foreach ($this->data as $source => $css) {
299
+ /*
300
+ * Let's first take out strings & comments, since we can't just remove
301
+ * whitespace anywhere. If whitespace occurs inside a string, we should
302
+ * leave it alone. E.g.:
303
+ * p { content: "a test" }
304
+ */
305
+ $this->extractStrings();
306
+ $this->stripComments();
307
+ $css = $this->replace($css);
308
+
309
+ $css = $this->stripWhitespace($css);
310
+ $css = $this->shortenHex($css);
311
+ $css = $this->shortenZeroes($css);
312
+ $css = $this->stripEmptyTags($css);
313
+
314
+ // restore the string we've extracted earlier
315
+ $css = $this->restoreExtractedData($css);
316
+
317
+ /*
318
+ * If we'll save to a new path, we'll have to fix the relative paths
319
+ * to be relative no longer to the source file, but to the new path.
320
+ * If we don't write to a file, fall back to same path so no
321
+ * conversion happens (because we still want it to go through most
322
+ * of the move code...)
323
+ */
324
+ $source = $source ?: '';
325
+ $converter = new Converter($source, $path ?: $source);
326
+ $css = $this->move($converter, $css);
327
+
328
+ // if no target path is given, relative paths were not converted, so
329
+ // they'll still be relative to the source file then
330
+ $css = $this->importFiles($path ?: $source, $css);
331
+ $css = $this->combineImports($path ?: $source, $css);
332
+
333
+ // combine css
334
+ $content .= $css;
335
+ }
336
+
337
+ $content = $this->moveImportsToTop($content);
338
+
339
+ return $content;
340
+ }
341
+
342
+ /**
343
+ * Moving a css file should update all relative urls.
344
+ * Relative references (e.g. ../images/image.gif) in a certain css file,
345
+ * will have to be updated when a file is being saved at another location
346
+ * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
347
+ *
348
+ * @param Converter $converter Relative path converter
349
+ * @param string $content The CSS content to update relative urls for.
350
+ *
351
+ * @return string
352
+ */
353
+ protected function move(Converter $converter, $content)
354
+ {
355
+ /*
356
+ * Relative path references will usually be enclosed by url(). @import
357
+ * is an exception, where url() is not necessary around the path (but is
358
+ * allowed).
359
+ * This *could* be 1 regular expression, where both regular expressions
360
+ * in this array are on different sides of a |. But we're using named
361
+ * patterns in both regexes, the same name on both regexes. This is only
362
+ * possible with a (?J) modifier, but that only works after a fairly
363
+ * recent PCRE version. That's why I'm doing 2 separate regular
364
+ * expressions & combining the matches after executing of both.
365
+ */
366
+ $relativeRegexes = array(
367
+ // url(xxx)
368
+ '/
369
+ # open url()
370
+ url\(
371
+
372
+ \s*
373
+
374
+ # open path enclosure
375
+ (?P<quotes>["\'])?
376
+
377
+ # fetch path
378
+ (?P<path>
379
+
380
+ # do not fetch data uris or external sources
381
+ (?!(
382
+ \s?
383
+ ["\']?
384
+ (data|https?):
385
+ ))
386
+
387
+ .+?
388
+ )
389
+
390
+ # close path enclosure
391
+ (?(quotes)(?P=quotes))
392
+
393
+ \s*
394
+
395
+ # close url()
396
+ \)
397
+
398
+ /ix',
399
+
400
+ // @import "xxx"
401
+ '/
402
+ # import statement
403
+ @import
404
+
405
+ # whitespace
406
+ \s+
407
+
408
+ # we don\'t have to check for @import url(), because the
409
+ # condition above will already catch these
410
+
411
+ # open path enclosure
412
+ (?P<quotes>["\'])
413
+
414
+ # fetch path
415
+ (?P<path>
416
+
417
+ # do not fetch data uris or external sources
418
+ (?!(
419
+ ["\']?
420
+ (data|https?):
421
+ ))
422
+
423
+ .+?
424
+ )
425
+
426
+ # close path enclosure
427
+ (?P=quotes)
428
+
429
+ /ix',
430
+ );
431
+
432
+ // find all relative urls in css
433
+ $matches = array();
434
+ foreach ($relativeRegexes as $relativeRegex) {
435
+ if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) {
436
+ $matches = array_merge($matches, $regexMatches);
437
+ }
438
+ }
439
+
440
+ $search = array();
441
+ $replace = array();
442
+
443
+ // loop all urls
444
+ foreach ($matches as $match) {
445
+ // determine if it's a url() or an @import match
446
+ $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
447
+
448
+ // fix relative url
449
+ $url = $converter->convert($match['path']);
450
+
451
+ // build replacement
452
+ $search[] = $match[0];
453
+ if ($type == 'url') {
454
+ $replace[] = 'url('.$url.')';
455
+ } elseif ($type == 'import') {
456
+ $replace[] = '@import "'.$url.'"';
457
+ }
458
+ }
459
+
460
+ // replace urls
461
+ $content = str_replace($search, $replace, $content);
462
+
463
+ return $content;
464
+ }
465
+
466
+ /**
467
+ * Shorthand hex color codes.
468
+ * #FF0000 -> #F00.
469
+ *
470
+ * @param string $content The CSS content to shorten the hex color codes for.
471
+ *
472
+ * @return string
473
+ */
474
+ protected function shortenHex($content)
475
+ {
476
+ $content = preg_replace('/(?<![\'"])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?![\'"])/i', '#$1$2$3', $content);
477
+
478
+ return $content;
479
+ }
480
+
481
+ /**
482
+ * Shorthand 0 values to plain 0, instead of e.g. -0em.
483
+ *
484
+ * @param string $content The CSS content to shorten the zero values for.
485
+ *
486
+ * @return string
487
+ */
488
+ protected function shortenZeroes($content)
489
+ {
490
+ // reusable bits of code throughout these regexes:
491
+ // before & after are used to make sure we don't match lose unintended
492
+ // 0-like values (e.g. in #000, or in http://url/1.0)
493
+ // units can be stripped from 0 values, or used to recognize non 0
494
+ // values (where wa may be able to strip a .0 suffix)
495
+ $before = '(?<=[:(, ])';
496
+ $after = '(?=[ ,);}])';
497
+ $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
498
+
499
+ // strip units after zeroes (0px -> 0)
500
+ // NOTE: it should be safe to remove all units for a 0 value, but in
501
+ // practice, Webkit (especially Safari) seems to stumble over at least
502
+ // 0%, potentially other units as well. Only stripping 'px' for now.
503
+ // @see https://github.com/matthiasmullie/minify/issues/60
504
+ $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
505
+
506
+ // strip 0-digits (.0 -> 0)
507
+ $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
508
+ // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
509
+ $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
510
+ // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
511
+ $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
512
+ // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
513
+ $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
514
+
515
+ // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
516
+ $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
517
+
518
+ return $content;
519
+ }
520
+
521
+ /**
522
+ * Strip comments from source code.
523
+ *
524
+ * @param string $content
525
+ * @return string
526
+ */
527
+ protected function stripEmptyTags($content)
528
+ {
529
+ return preg_replace('/(^|\})[^\{]+\{\s*\}/', '\\1', $content);
530
+ }
531
+
532
+ /**
533
+ * Strip comments from source code.
534
+ */
535
+ protected function stripComments()
536
+ {
537
+ $this->registerPattern('/\/\*.*?\*\//s', '');
538
+ }
539
+
540
+ /**
541
+ * Strip whitespace.
542
+ *
543
+ * @param string $content The CSS content to strip the whitespace for.
544
+ *
545
+ * @return string
546
+ */
547
+ protected function stripWhitespace($content)
548
+ {
549
+ // remove leading & trailing whitespace
550
+ $content = preg_replace('/^\s*/m', '', $content);
551
+ $content = preg_replace('/\s*$/m', '', $content);
552
+
553
+ // replace newlines with a single space
554
+ $content = preg_replace('/\s+/', ' ', $content);
555
+
556
+ // remove whitespace around meta characters
557
+ // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
558
+ $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
559
+ $content = preg_replace('/([\[(:])\s+/', '$1', $content);
560
+ $content = preg_replace('/\s+([\]\)])/', '$1', $content);
561
+ $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
562
+
563
+ // whitespace around + and - can only be stripped in selectors, like
564
+ // :nth-child(3+2n), not in things like calc(3px + 2px) or shorthands
565
+ // like 3px -2px
566
+ $content = preg_replace('/\s*([+-])\s*(?=[^}]*{)/', '$1', $content);
567
+
568
+ // remove semicolon/whitespace followed by closing bracket
569
+ $content = str_replace(';}', '}', $content);
570
+
571
+ return trim($content);
572
+ }
573
+ }
minify/src/Converter.php ADDED
@@ -0,0 +1,195 @@