PageRenderTime 30ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/public_html/wp-content/plugins/autoptimize/classes/autoptimizeStyles.php

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