PluginProbe
Autoptimize / 2.1.2
Autoptimize v2.1.2
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / autoptimizeStyles.php

autoptimizeStyles.php in Autoptimize 2.1.2, at classes/autoptimizeStyles.php

663 lines 30.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4 class autoptimizeStyles extends autoptimizeBase {
5 private $css = array();
6 private $csscode = array();
7 private $url = array();
8 private $restofcontent = '';
9 private $mhtml = '';
10 private $datauris = false;
11 private $hashmap = array();
12 private $alreadyminified = false;
13 private $inline = false;
14 private $defer = false;
15 private $defer_inline = false;
16 private $whitelist = '';
17 private $cssinlinesize = '';
18 private $cssremovables = array();
19 private $include_inline = false;
20 private $inject_min_late = '';
21
22 //Reads the page and collects style tags
23 public function read($options) {
24 $noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
25 if ($noptimizeCSS) return false;
26
27 $whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '' );
28 if (!empty($whitelistCSS)) {
29 $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS)));
30 }
31
32 if ($options['nogooglefont'] == true) {
33 $removableCSS = "fonts.googleapis.com";
34 } else {
35 $removableCSS = "";
36 }
37 $removableCSS = apply_filters( 'autoptimize_filter_css_removables', $removableCSS);
38 if (!empty($removableCSS)) {
39 $this->cssremovables = array_filter(array_map('trim',explode(",",$removableCSS)));
40 }
41
42 $this->cssinlinesize = apply_filters('autoptimize_filter_css_inlinesize',256);
43
44 // filter to "late inject minified CSS", default to true for now (it is faster)
45 $this->inject_min_late = apply_filters('autoptimize_filter_css_inject_min_late',true);
46
47 // Remove everything that's not the header
48 if ( apply_filters('autoptimize_filter_css_justhead',$options['justhead']) == true ) {
49 $content = explode('</head>',$this->content,2);
50 $this->content = $content[0].'</head>';
51 $this->restofcontent = $content[1];
52 }
53
54 // include inline?
55 if( apply_filters('autoptimize_css_include_inline',$options['include_inline']) == true ) {
56 $this->include_inline = true;
57 }
58
59 // what CSS shouldn't be autoptimized
60 $excludeCSS = $options['css_exclude'];
61 $excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $excludeCSS );
62 if ($excludeCSS!=="") {
63 $this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS)));
64 } else {
65 $this->dontmove = "";
66 }
67
68 // should we defer css?
69 // value: true/ false
70 $this->defer = $options['defer'];
71 $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer );
72
73 // should we inline while deferring?
74 // value: inlined CSS
75 $this->defer_inline = $options['defer_inline'];
76 $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->defer_inline );
77
78 // should we inline?
79 // value: true/ false
80 $this->inline = $options['inline'];
81 $this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline );
82
83 // get cdn url
84 $this->cdn_url = $options['cdn_url'];
85
86 // Store data: URIs setting for later use
87 $this->datauris = $options['datauris'];
88
89 // noptimize me
90 $this->content = $this->hide_noptimize($this->content);
91
92 // exclude (no)script, as those may contain CSS which should be left as is
93 if ( strpos( $this->content, '<script' ) !== false ) {
94 $this->content = preg_replace_callback(
95 '#<(?:no)?script.*?<\/(?:no)?script>#is',
96 create_function(
97 '$matches',
98 'return "%%SCRIPT".AUTOPTIMIZE_HASH."%%".base64_encode($matches[0])."%%SCRIPT%%";'
99 ),
100 $this->content
101 );
102 }
103
104 // Save IE hacks
105 $this->content = $this->hide_iehacks($this->content);
106
107 // hide comments
108 $this->content = $this->hide_comments($this->content);
109
110 // Get <style> and <link>
111 if(preg_match_all('#(<style[^>]*>.*</style>)|(<link[^>]*stylesheet[^>]*>)#Usmi',$this->content,$matches)) {
112 foreach($matches[0] as $tag) {
113 if ($this->isremovable($tag,$this->cssremovables)) {
114 $this->content = str_replace($tag,'',$this->content);
115 } else if ($this->ismovable($tag)) {
116 // Get the media
117 if(strpos($tag,'media=')!==false) {
118 preg_match('#media=(?:"|\')([^>]*)(?:"|\')#Ui',$tag,$medias);
119 $medias = explode(',',$medias[1]);
120 $media = array();
121 foreach($medias as $elem) {
122 if (empty($elem)) { $elem="all"; }
123 $media[] = $elem;
124 }
125 } else {
126 // No media specified - applies to all
127 $media = array('all');
128 }
129 $media = apply_filters( 'autoptimize_filter_css_tagmedia',$media,$tag );
130
131 if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
132 // <link>
133 $url = current(explode('?',$source[2],2));
134 $path = $this->getpath($url);
135
136 if($path!==false && preg_match('#\.css$#',$path)) {
137 // Good link
138 $this->css[] = array($media,$path);
139 }else{
140 // Link is dynamic (.php etc)
141 $tag = '';
142 }
143 } else {
144 // inline css in style tags can be wrapped in comment tags, so restore comments
145 $tag = $this->restore_comments($tag);
146 preg_match('#<style.*>(.*)</style>#Usmi',$tag,$code);
147
148 // and re-hide them to be able to to the removal based on tag
149 $tag = $this->hide_comments($tag);
150
151 if ( $this->include_inline ) {
152 $code = preg_replace('#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm','$1',$code[1]);
153 $this->css[] = array($media,'INLINE;'.$code);
154 } else {
155 $tag = '';
156 }
157 }
158
159 // Remove the original style tag
160 $this->content = str_replace($tag,'',$this->content);
161 }
162 }
163 return true;
164 }
165 // Really, no styles?
166 return false;
167 }
168
169 // Joins and optimizes CSS
170 public function minify() {
171 foreach($this->css as $group) {
172 list($media,$css) = $group;
173 if(preg_match('#^INLINE;#',$css)) {
174 // <style>
175 $css = preg_replace('#^INLINE;#','',$css);
176 $css = $this->fixurls(ABSPATH.'/index.php',$css);
177 $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, "" );
178 if ( has_filter('autoptimize_css_individual_style') && !empty($tmpstyle) ) {
179 $css=$tmpstyle;
180 $this->alreadyminified=true;
181 }
182 } else {
183 //<link>
184 if($css !== false && file_exists($css) && is_readable($css)) {
185 $cssPath = $css;
186 $css = $this->fixurls($cssPath,file_get_contents($cssPath));
187 $css = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$css);
188 $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath );
189 if (has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
190 $css=$tmpstyle;
191 $this->alreadyminified=true;
192 } else if ($this->can_inject_late($cssPath,$css)) {
193 $css="%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($cssPath)."|".md5($css)."%%INJECTLATER%%";
194 }
195 } else {
196 // Couldn't read CSS. Maybe getpath isn't working?
197 $css = '';
198 }
199 }
200
201 foreach($media as $elem) {
202 if(!isset($this->csscode[$elem]))
203 $this->csscode[$elem] = '';
204 $this->csscode[$elem] .= "\n/*FILESTART*/".$css;
205 }
206 }
207
208 // Check for duplicate code
209 $md5list = array();
210 $tmpcss = $this->csscode;
211 foreach($tmpcss as $media => $code) {
212 $md5sum = md5($code);
213 $medianame = $media;
214 foreach($md5list as $med => $sum) {
215 // If same code
216 if($sum === $md5sum) {
217 //Add the merged code
218 $medianame = $med.', '.$media;
219 $this->csscode[$medianame] = $code;
220 $md5list[$medianame] = $md5list[$med];
221 unset($this->csscode[$med], $this->csscode[$media]);
222 unset($md5list[$med]);
223 }
224 }
225 $md5list[$medianame] = $md5sum;
226 }
227 unset($tmpcss);
228
229 // Manage @imports, while is for recursive import management
230 foreach ($this->csscode as &$thiscss) {
231 // Flag to trigger import reconstitution and var to hold external imports
232 $fiximports = false;
233 $external_imports = "";
234
235 // remove comments to avoid importing commented-out imports
236 $thiscss_nocomments=preg_replace('#/\*.*\*/#Um','',$thiscss);
237 while(preg_match_all('#@import.*(?:;|$)#Um',$thiscss_nocomments,$matches)) {
238 foreach($matches[0] as $import) {
239 if ($this->isremovable($import,$this->cssremovables)) {
240 $thiscss = str_replace($import,'',$thiscss);
241 $import_ok = true;
242 } else {
243 $url = trim(preg_replace('#^.*((?:https?:|ftp:)?//.*\.css).*$#','$1',trim($import))," \t\n\r\0\x0B\"'");
244 $path = $this->getpath($url);
245 $import_ok = false;
246 if (file_exists($path) && is_readable($path)) {
247 $code = addcslashes($this->fixurls($path,file_get_contents($path)),"\\");
248 $code = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$code);
249 $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $code, "" );
250 if ( has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
251 $code=$tmpstyle;
252 $this->alreadyminified=true;
253 } else if ($this->can_inject_late($path,$code)) {
254 $code="%%INJECTLATER%%".base64_encode($path)."|".md5($code)."%%INJECTLATER%%";
255 }
256
257 if(!empty($code)) {
258 $tmp_thiscss = preg_replace('#(/\*FILESTART\*/.*)'.preg_quote($import,'#').'#Us','/*FILESTART2*/'.$code.'$1',$thiscss);
259 if (!empty($tmp_thiscss)) {
260 $thiscss = $tmp_thiscss;
261 $import_ok = true;
262 unset($tmp_thiscss);
263 }
264 unset($code);
265 }
266 }
267 }
268
269 if (!$import_ok) {
270 // external imports and general fall-back
271 $external_imports .= $import;
272 $thiscss = str_replace($import,'',$thiscss);
273 $fiximports = true;
274 }
275 }
276 $thiscss = preg_replace('#/\*FILESTART\*/#','',$thiscss);
277 $thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
278
279 // and update $thiscss_nocomments before going into next iteration in while loop
280 $thiscss_nocomments=preg_replace('#/\*.*\*/#Um','',$thiscss);
281 }
282 unset($thiscss_nocomments);
283
284 // add external imports to top of aggregated CSS
285 if($fiximports) {
286 $thiscss=$external_imports.$thiscss;
287 }
288 }
289 unset($thiscss);
290
291 // $this->csscode has all the uncompressed code now.
292 $mhtmlcount = 0;
293 foreach($this->csscode as &$code) {
294 // Check for already-minified code
295 $hash = md5($code);
296 $ccheck = new autoptimizeCache($hash,'css');
297 if($ccheck->check()) {
298 $code = $ccheck->retrieve();
299 $this->hashmap[md5($code)] = $hash;
300 continue;
301 }
302 unset($ccheck);
303
304 // Do the imaging!
305 $imgreplace = array();
306 preg_match_all('#(background[^;{}]*url\((?!\s?"?\'?\s?data)(.*)\)[^;}]*)(?:;|$|})#Usm',$code,$matches);
307
308 if(($this->datauris == true) && (function_exists('base64_encode')) && (is_array($matches))) {
309 foreach($matches[2] as $count => $quotedurl) {
310 $iurl = trim($quotedurl," \t\n\r\0\x0B\"'");
311
312 // if querystring, remove it from url
313 if (strpos($iurl,'?') !== false) { $iurl = strtok($iurl,'?'); }
314
315 $ipath = $this->getpath($iurl);
316
317 $datauri_max_size = 4096;
318 $datauri_max_size = (int) apply_filters( 'autoptimize_filter_css_datauri_maxsize', $datauri_max_size );
319 $datauri_exclude = apply_filters( 'autoptimize_filter_css_datauri_exclude', "");
320 if (!empty($datauri_exclude)) {
321 $no_datauris=array_filter(array_map('trim',explode(",",$datauri_exclude)));
322 foreach ($no_datauris as $no_datauri) {
323 if (strpos($iurl,$no_datauri)!==false) {
324 $ipath=false;
325 break;
326 }
327 }
328 }
329
330 if($ipath != false && preg_match('#\.(jpe?g|png|gif|bmp)$#i',$ipath) && file_exists($ipath) && is_readable($ipath) && filesize($ipath) <= $datauri_max_size) {
331 $ihash=md5($ipath);
332 $icheck = new autoptimizeCache($ihash,'img');
333 if($icheck->check()) {
334 // we have the base64 image in cache
335 $headAndData=$icheck->retrieve();
336 $_base64data=explode(";base64,",$headAndData);
337 $base64data=$_base64data[1];
338 } else {
339 // It's an image and we don't have it in cache, get the type
340 $explA=explode('.',$ipath);
341 $type=end($explA);
342
343 switch($type) {
344 case 'jpeg':
345 $dataurihead = 'data:image/jpeg;base64,';
346 break;
347 case 'jpg':
348 $dataurihead = 'data:image/jpeg;base64,';
349 break;
350 case 'gif':
351 $dataurihead = 'data:image/gif;base64,';
352 break;
353 case 'png':
354 $dataurihead = 'data:image/png;base64,';
355 break;
356 case 'bmp':
357 $dataurihead = 'data:image/bmp;base64,';
358 break;
359 default:
360 $dataurihead = 'data:application/octet-stream;base64,';
361 }
362
363 // Encode the data
364 $base64data = base64_encode(file_get_contents($ipath));
365 $headAndData=$dataurihead.$base64data;
366
367 // Save in cache
368 $icheck->cache($headAndData,"text/plain");
369 }
370 unset($icheck);
371
372 // Add it to the list for replacement
373 $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].';';
374
375 // Store image on the mhtml document
376 $this->mhtml .= "--_\r\nContent-Location:{$mhtmlcount}\r\nContent-Transfer-Encoding:base64\r\n\r\n{$base64data}\r\n";
377 $mhtmlcount++;
378 } else {
379 // just cdn the URL if applicable
380 if (!empty($this->cdn_url)) {
381 $url = trim($quotedurl," \t\n\r\0\x0B\"'");
382 $cdn_url=$this->url_replace_cdn($url);
383 $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$cdn_url,$matches[1][$count]);
384 }
385 }
386 }
387 } else if ((is_array($matches)) && (!empty($this->cdn_url))) {
388 // change background image urls to cdn-url
389 foreach($matches[2] as $count => $quotedurl) {
390 $url = trim($quotedurl," \t\n\r\0\x0B\"'");
391 $cdn_url=$this->url_replace_cdn($url);
392 $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$cdn_url,$matches[1][$count]);
393 }
394 }
395
396 if(!empty($imgreplace)) {
397 $code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code);
398 }
399
400 // CDN the fonts!
401 if ( (!empty($this->cdn_url)) && (apply_filters('autoptimize_filter_css_fonts_cdn',false)) && (version_compare(PHP_VERSION, '5.3.0') >= 0) ) {
402 $fontreplace = array();
403 include_once(AUTOPTIMIZE_PLUGIN_DIR.'classlesses/autoptimizeFontRegex.php');
404
405 preg_match_all($fonturl_regex,$code,$matches);
406 if (is_array($matches)) {
407 foreach($matches[8] as $count => $quotedurl) {
408 $url = trim($quotedurl," \t\n\r\0\x0B\"'");
409 $cdn_url=$this->url_replace_cdn($url);
410 $fontreplace[$matches[8][$count]] = str_replace($quotedurl,$cdn_url,$matches[8][$count]);
411 }
412 if(!empty($fontreplace)) {
413 $code = str_replace(array_keys($fontreplace),array_values($fontreplace),$code);
414 }
415 }
416 }
417
418 // Minify
419 if (($this->alreadyminified!==true) && (apply_filters( "autoptimize_css_do_minify", true))) {
420 if (class_exists('Minify_CSS_Compressor')) {
421 $tmp_code = trim(Minify_CSS_Compressor::process($code));
422 } else if(class_exists('CSSmin')) {
423 $cssmin = new CSSmin();
424 if (method_exists($cssmin,"run")) {
425 $tmp_code = trim($cssmin->run($code));
426 } elseif (@is_callable(array($cssmin,"minify"))) {
427 $tmp_code = trim(CssMin::minify($code));
428 }
429 }
430 if (!empty($tmp_code)) {
431 $code = $tmp_code;
432 unset($tmp_code);
433 }
434 }
435
436 $code = $this->inject_minified($code);
437
438 $tmp_code = apply_filters( 'autoptimize_css_after_minify',$code );
439 if (!empty($tmp_code)) {
440 $code = $tmp_code;
441 unset($tmp_code);
442 }
443
444 $this->hashmap[md5($code)] = $hash;
445 }
446 unset($code);
447 return true;
448 }
449
450 //Caches the CSS in uncompressed, deflated and gzipped form.
451 public function cache() {
452 if($this->datauris) {
453 // MHTML Preparation
454 $this->mhtml = "/*\r\nContent-Type: multipart/related; boundary=\"_\"\r\n\r\n".$this->mhtml."*/\r\n";
455 $md5 = md5($this->mhtml);
456 $cache = new autoptimizeCache($md5,'txt');
457 if(!$cache->check()) {
458 // Cache our images for IE
459 $cache->cache($this->mhtml,'text/plain');
460 }
461 $mhtml = AUTOPTIMIZE_CACHE_URL.$cache->getname();
462 }
463
464 // CSS cache
465 foreach($this->csscode as $media => $code) {
466 $md5 = $this->hashmap[md5($code)];
467
468 if($this->datauris) {
469 // Images for ie! Get the right url
470 $code = str_replace('%%MHTML%%',$mhtml,$code);
471 }
472
473 $cache = new autoptimizeCache($md5,'css');
474 if(!$cache->check()) {
475 // Cache our code
476 $cache->cache($code,'text/css');
477 }
478 $this->url[$media] = AUTOPTIMIZE_CACHE_URL.$cache->getname();
479 }
480 }
481
482 //Returns the content
483 public function getcontent() {
484 // restore IE hacks
485 $this->content = $this->restore_iehacks($this->content);
486
487 // restore comments
488 $this->content = $this->restore_comments($this->content);
489
490 // restore (no)script
491 if ( strpos( $this->content, '%%SCRIPT%%' ) !== false ) {
492 $this->content = preg_replace_callback(
493 '#%%SCRIPT'.AUTOPTIMIZE_HASH.'%%(.*?)%%SCRIPT%%#is',
494 create_function(
495 '$matches',
496 'return base64_decode($matches[1]);'
497 ),
498 $this->content
499 );
500 }
501
502 // restore noptimize
503 $this->content = $this->restore_noptimize($this->content);
504
505 //Restore the full content
506 if(!empty($this->restofcontent)) {
507 $this->content .= $this->restofcontent;
508 $this->restofcontent = '';
509 }
510
511 // Inject the new stylesheets
512 $replaceTag = array("<title","before");
513 $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag );
514
515 if ($this->inline == true) {
516 foreach($this->csscode as $media => $code) {
517 $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$code.'</style>',$replaceTag);
518 }
519 } else {
520 if ($this->defer == true) {
521 $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.getElementById('aonoscrcss');aoin.parentNode.insertBefore(l,aoin.nextSibling);}function deferredCSS() {";
522 $noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
523 $defer_inline_code=$this->defer_inline;
524 if(!empty($defer_inline_code)){
525 if ( apply_filters( 'autoptimize_filter_css_critcss_minify',true ) ) {
526 $iCssHash=md5($defer_inline_code);
527 $iCssCache = new autoptimizeCache($iCssHash,'css');
528 if($iCssCache->check()) {
529 // we have the optimized inline CSS in cache
530 $defer_inline_code=$iCssCache->retrieve();
531 } else {
532 if (class_exists('Minify_CSS_Compressor')) {
533 $tmp_code = trim(Minify_CSS_Compressor::process($defer_inline_code));
534 } else if(class_exists('CSSmin')) {
535 $cssmin = new CSSmin();
536 $tmp_code = trim($cssmin->run($defer_inline_code));
537 }
538 if (!empty($tmp_code)) {
539 $defer_inline_code = $tmp_code;
540 $iCssCache->cache($defer_inline_code,"text/css");
541 unset($tmp_code);
542 }
543 }
544 }
545 $code_out='<style type="text/css" id="aoatfcss" media="all">'.$defer_inline_code.'</style>';
546 $this->inject_in_html($code_out,$replaceTag);
547 }
548 }
549
550 foreach($this->url as $media => $url) {
551 $url = $this->url_replace_cdn($url);
552
553 //Add the stylesheet either deferred (import at bottom) or normal links in head
554 if($this->defer == true) {
555 $deferredCssBlock .= "lCss('".$url."','".$media."');";
556 $noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />';
557 } else {
558 if (strlen($this->csscode[$media]) > $this->cssinlinesize) {
559 $this->inject_in_html('<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />',$replaceTag);
560 } else if (strlen($this->csscode[$media])>0) {
561 $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$this->csscode[$media].'</style>',$replaceTag);
562 }
563 }
564 }
565
566 if($this->defer == true) {
567 $deferredCssBlock .= "}if(window.addEventListener){window.addEventListener('DOMContentLoaded',deferredCSS,false);}else{window.onload = deferredCSS;}</script>";
568 $noScriptCssBlock .= "</noscript>";
569 $this->inject_in_html($noScriptCssBlock,$replaceTag);
570 $this->inject_in_html($deferredCssBlock,array('</body>','before'));
571 }
572 }
573
574 //Return the modified stylesheet
575 return $this->content;
576 }
577
578 static function fixurls($file,$code) {
579 $file = str_replace(WP_ROOT_DIR,'/',$file);
580 $dir = dirname($file); //Like /wp-content
581
582 // quick fix for import-troubles in e.g. arras theme
583 $code=preg_replace('#@import ("|\')(.+?)\.css("|\')#','@import url("${2}.css")',$code);
584
585 if(preg_match_all('#url\((?!data)(?!\#)(?!"\#)(.*)\)#Usi',$code,$matches)) {
586 $replace = array();
587 foreach($matches[1] as $k => $url) {
588 // Remove quotes
589 $url = trim($url," \t\n\r\0\x0B\"'");
590 $noQurl = trim($url,"\"'");
591 if ($url!==$noQurl) {
592 $removedQuotes=true;
593 } else {
594 $removedQuotes=false;
595 }
596 $url=$noQurl;
597 if(substr($url,0,1)=='/' || preg_match('#^(https?://|ftp://|data:)#i',$url)) {
598 //URL is absolute
599 continue;
600 } else {
601 // relative URL
602 $newurl = preg_replace('/https?:/','',str_replace(" ","%20",AUTOPTIMIZE_WP_ROOT_URL.str_replace('//','/',$dir.'/'.$url)));
603
604 $hash = md5($url);
605 $code = str_replace($matches[0][$k],$hash,$code);
606
607 if (!empty($removedQuotes)) {
608 $replace[$hash] = 'url(\''.$newurl.'\')';
609 } else {
610 $replace[$hash] = 'url('.$newurl.')';
611 }
612 }
613 }
614 //Do the replacing here to avoid breaking URLs
615 $code = str_replace(array_keys($replace),array_values($replace),$code);
616 }
617 return $code;
618 }
619
620 private function ismovable($tag) {
621 if (!empty($this->whitelist)) {
622 foreach ($this->whitelist as $match) {
623 if(strpos($tag,$match)!==false) {
624 return true;
625 }
626 }
627 // no match with whitelist
628 return false;
629 } else {
630 if (is_array($this->dontmove)) {
631 foreach($this->dontmove as $match) {
632 if(strpos($tag,$match)!==false) {
633 //Matched something
634 return false;
635 }
636 }
637 }
638
639 //If we're here it's safe to move
640 return true;
641 }
642 }
643
644 private function can_inject_late($cssPath,$css) {
645 if ((strpos($cssPath,"min.css")===false) || ($this->inject_min_late!==true)) {
646 // late-inject turned off or file not minified based on filename
647 return false;
648 } else if (strpos($css,"@import")!==false) {
649 // can't late-inject files with imports as those need to be aggregated
650 return false;
651 } else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("autoptimize_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) {
652 // don't late-inject CSS with font-src's if fonts are set to be CDN'ed
653 return false;
654 } else if ( (($this->datauris == true) || (!empty($this->cdn_url))) && preg_match("#background[^;}]*url\(#Ui",$css) ) {
655 // don't late-inject CSS with images if CDN is set OR is image inlining is on
656 return false;
657 } else {
658 // phew, all is safe, we can late-inject
659 return true;
660 }
661 }
662 }
663