array(), 'last' => array()); private $restofcontent = ''; private $md5hash = ''; private $whitelist = ''; private $jsremovables = array(); private $inject_min_late = ''; //Reads the page and collects script tags public function read($options) { $noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content ); if ($noptimizeJS) return false; // only optimize known good JS? $whitelistJS = apply_filters( 'autoptimize_filter_js_whitelist', "" ); if (!empty($whitelistJS)) { $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistJS))); } // is there JS we should simply remove $removableJS = apply_filters( 'autoptimize_filter_js_removables', ''); if (!empty($removableJS)) { $this->jsremovables = array_filter(array_map('trim',explode(",",$removableJS))); } // only header? if( apply_filters('autoptimize_filter_js_justhead',$options['justhead']) == true ) { $content = explode('',$this->content,2); $this->content = $content[0].''; $this->restofcontent = $content[1]; } // include inline? if( apply_filters('autoptimize_js_include_inline',$options['include_inline']) == true ) { $this->include_inline = true; } // filter to "late inject minified JS", default to true for now (it is faster) $this->inject_min_late = apply_filters('autoptimize_filter_js_inject_min_late',true); // filters to override hardcoded do(nt)move(last) array contents (array in, array out!) $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove ); $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast ); $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove ); // get extra exclusions settings or filter $excludeJS = $options['js_exclude']; $excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS ); if ($excludeJS!=="") { $exclJSArr = array_filter(array_map('trim',explode(",",$excludeJS))); $this->dontmove = array_merge($exclJSArr,$this->dontmove); } //Should we add try-catch? if($options['trycatch'] == true) $this->trycatch = true; // force js in head? if($options['forcehead'] == true) { $this->forcehead = true; } else { $this->forcehead = false; } $this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead ); // get cdn url $this->cdn_url = $options['cdn_url']; // noptimize me $this->content = $this->hide_noptimize($this->content); // Save IE hacks $this->content = $this->hide_iehacks($this->content); // comments $this->content = $this->hide_comments($this->content); //Get script files if(preg_match_all('##Usmi',$this->content,$matches)) { foreach($matches[0] as $tag) { // only consider aggregation whitelisted in should_aggregate-function if( !$this->should_aggregate($tag) ) { $tag=''; continue; } if(preg_match('#]*src=("|\')([^>]*)("|\')#Usmi',$tag,$source)) { if ($this->isremovable($tag,$this->jsremovables)) { $this->content = str_replace($tag,'',$this->content); continue; } // External script $url = current(explode('?',$source[2],2)); $path = $this->getpath($url); if($path !== false && preg_match('#\.js$#',$path)) { //Inline if($this->ismergeable($tag)) { //We can merge it $this->scripts[] = $path; } else { //No merge, but maybe we can move it if($this->ismovable($tag)) { //Yeah, move it if($this->movetolast($tag)) { $this->move['last'][] = $tag; } else { $this->move['first'][] = $tag; } } else { //We shouldn't touch this $tag = ''; } } } else { //External script (example: google analytics) //OR Script is dynamic (.php etc) if($this->ismovable($tag)) { if($this->movetolast($tag)) { $this->move['last'][] = $tag; } else { $this->move['first'][] = $tag; } } else { //We shouldn't touch this $tag = ''; } } } else { // Inline script if ($this->isremovable($tag,$this->jsremovables)) { $this->content = str_replace($tag,'',$this->content); continue; } // unhide comments, as javascript may be wrapped in comment-tags for old times' sake $tag = $this->restore_comments($tag); if($this->ismergeable($tag) && ( $this->include_inline )) { preg_match('#(.*)#Usmi',$tag,$code); $code = preg_replace('#.*.*#sm','$1',$code[1]); $code = preg_replace('/(?:^\\s*\\s*$)/','',$code); $this->scripts[] = 'INLINE;'.$code; } else { // Can we move this? if($this->ismovable($tag)) { if($this->movetolast($tag)) { $this->move['last'][] = $tag; } else { $this->move['first'][] = $tag; } } else { //We shouldn't touch this $tag = ''; } } // re-hide comments to be able to do the removal based on tag from $this->content $tag = $this->hide_comments($tag); } //Remove the original script tag $this->content = str_replace($tag,'',$this->content); } return true; } // No script files, great ;-) return false; } //Joins and optimizes JS public function minify() { foreach($this->scripts as $script) { if(preg_match('#^INLINE;#',$script)) { //Inline script $script = preg_replace('#^INLINE;#','',$script); $script = rtrim( $script, ";\n\t\r" ) . ';'; //Add try-catch? if($this->trycatch) { $script = 'try{'.$script.'}catch(e){}'; } $tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, "" ); if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscript) ) { $script=$tmpscript; $this->alreadyminified=true; } $this->jscode .= "\n" . $script; } else { //External script if($script !== false && file_exists($script) && is_readable($script)) { $scriptsrc = file_get_contents($script); $scriptsrc = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$scriptsrc); $scriptsrc = rtrim($scriptsrc,";\n\t\r").';'; //Add try-catch? if($this->trycatch) { $scriptsrc = 'try{'.$scriptsrc.'}catch(e){}'; } $tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script ); if ( has_filter('autoptimize_js_individual_script') && !empty($tmpscriptsrc) ) { $scriptsrc=$tmpscriptsrc; $this->alreadyminified=true; } else if ((( strpos($script,"min.js") !== false ) || ( strpos($script,"wp-includes/js/jquery/jquery.js") !== false )) && ( $this->inject_min_late === true )) { $scriptsrc="%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($script)."|".md5($scriptsrc)."%%INJECTLATER%%"; } $this->jscode .= "\n".$scriptsrc; }/*else{ //Couldn't read JS. Maybe getpath isn't working? }*/ } } //Check for already-minified code $this->md5hash = md5($this->jscode); $ccheck = new autoptimizeCache($this->md5hash,'js'); if($ccheck->check()) { $this->jscode = $ccheck->retrieve(); return true; } unset($ccheck); //$this->jscode has all the uncompressed code now. if ($this->alreadyminified!==true) { if (class_exists('JSMin') && apply_filters( 'autoptimize_js_do_minify' , true)) { if (@is_callable(array("JSMin","minify"))) { $tmp_jscode = trim(JSMin::minify($this->jscode)); if (!empty($tmp_jscode)) { $this->jscode = $tmp_jscode; unset($tmp_jscode); } $this->jscode = $this->inject_minified($this->jscode); $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode ); return true; } else { $this->jscode = $this->inject_minified($this->jscode); return false; } } else { $this->jscode = $this->inject_minified($this->jscode); return false; } } return true; } //Caches the JS in uncompressed, deflated and gzipped form. public function cache() { $cache = new autoptimizeCache($this->md5hash,'js'); if(!$cache->check()) { //Cache our code $cache->cache($this->jscode,'text/javascript'); } $this->url = AUTOPTIMIZE_CACHE_URL.$cache->getname(); $this->url = $this->url_replace_cdn($this->url); } // Returns the content public function getcontent() { // Restore the full content if(!empty($this->restofcontent)) { $this->content .= $this->restofcontent; $this->restofcontent = ''; } // Add the scripts taking forcehead/ deferred (default) into account if($this->forcehead == true) { $replaceTag=array("","before"); $defer=""; } else { $replaceTag=array("","before"); $defer="defer "; } $defer = apply_filters( 'autoptimize_filter_js_defer', $defer ); $bodyreplacementpayload = ''; $bodyreplacementpayload = apply_filters('autoptimize_filter_js_bodyreplacementpayload',$bodyreplacementpayload); $bodyreplacement = implode('',$this->move['first']); $bodyreplacement .= $bodyreplacementpayload; $bodyreplacement .= implode('',$this->move['last']); $replaceTag = apply_filters( 'autoptimize_filter_js_replacetag', $replaceTag ); if (strlen($this->jscode)>0) { $this->inject_in_html($bodyreplacement,$replaceTag); } // restore comments $this->content = $this->restore_comments($this->content); // Restore IE hacks $this->content = $this->restore_iehacks($this->content); // Restore noptimize $this->content = $this->restore_noptimize($this->content); // Return the modified HTML return $this->content; } // Checks against the white- and blacklists private function ismergeable($tag) { if (!empty($this->whitelist)) { foreach ($this->whitelist as $match) { if(strpos($tag,$match)!==false) { return true; } } // no match with whitelist return false; } else { foreach($this->domove as $match) { if(strpos($tag,$match)!==false) { // Matched something return false; } } if ($this->movetolast($tag)) { return false; } foreach($this->dontmove as $match) { if(strpos($tag,$match)!==false) { //Matched something return false; } } // If we're here it's safe to merge return true; } } // Checks againstt the blacklist private function ismovable($tag) { if ($this->include_inline !== true || apply_filters('autoptimize_filter_js_unmovable',true)) { return false; } foreach($this->domove as $match) { if(strpos($tag,$match)!==false) { // Matched something return true; } } if ($this->movetolast($tag)) { return true; } foreach($this->dontmove as $match) { if(strpos($tag,$match)!==false) { // Matched something return false; } } // If we're here it's safe to move return true; } private function movetolast($tag) { foreach($this->domovelast as $match) { if(strpos($tag,$match)!==false) { // Matched, return true return true; } } // Should be in 'first' return false; } /** * Determines wheter a