array(), 'last' => array() ); private $dontmove = array( 'document.write','html5.js','show_ads.js','google_ad','histats.com/js','statcounter.com/counter/counter.js', 'ws.amazon.com/widgets','media.fastclick.net','/ads/','comment-form-quicktags/quicktags.php','edToolbar', 'intensedebate.com','scripts.chitika.net/','_gaq.push','jotform.com/','admin-bar.min.js','GoogleAnalyticsObject', 'plupload.full.min.js','syntaxhighlighter','adsbygoogle','gist.github.com','_stq','nonce','post_id','data-noptimize' ,'logHuman' ); private $domove = array( 'gaJsHost','load_cmc','jd.gallery.transitions.js','swfobject.embedSWF(','tiny_mce.js','tinyMCEPreInit.go' ); private $domovelast = array( 'addthis.com','/afsonline/show_afs_search.js','disqus.js','networkedblogs.com/getnetworkwidget','infolinks.com/js/', 'jd.gallery.js.php','jd.gallery.transitions.js','swfobject.embedSWF(','linkwithin.com/widget.js','tiny_mce.js','tinyMCEPreInit.go' ); public $cdn_url = ''; private $aggregate = true; private $trycatch = false; private $alreadyminified = false; private $forcehead = true; private $include_inline = false; private $jscode = ''; private $url = ''; private $restofcontent = ''; private $md5hash = ''; private $whitelist = ''; private $jsremovables = array(); private $inject_min_late = ''; private $minify_excluded = true; // 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', '', $this->content ); 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', '', $this->content ); if (!empty($removableJS)) { $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) ); } // only header? if ( apply_filters( 'autoptimize_filter_js_justhead', $options['justhead'] ) ) { $content = explode( '', $this->content, 2 ); $this->content = $content[0] . ''; $this->restofcontent = $content[1]; } // Determine whether we're doing JS-files aggregation or not. if ( ! $options['aggregate'] ) { $this->aggregate = false; } // Returning true for "dontaggregate" turns off aggregation. if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) { $this->aggregate = false; } // include inline? if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) { $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 ); // Determine whether excluded files should be minified if not yet so. if ( ! $options['minify_excluded'] && $options['aggregate'] ) { $this->minify_excluded = false; } // get extra exclusions settings or filter. $excludeJS = $options['js_exclude']; $excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS, $this->content ); if ( '' !== $excludeJS ) { if ( is_array( $excludeJS ) ) { if ( ( $removeKeys = array_keys( $excludeJS, 'remove' ) ) !== false ) { foreach ( $removeKeys as $removeKey ) { unset( $excludeJS[$removeKey] ); $this->jsremovables[] = $removeKey; } } $exclJSArr = array_keys( $excludeJS ); } else { $exclJSArr = array_filter( array_map( 'trim', explode( ',', $excludeJS ) ) ); } $this->dontmove = array_merge( $exclJSArr, $this->dontmove ); } // Should we add try-catch? if ( $options['trycatch'] ) { $this->trycatch = true; } // force js in head? if ( $options['forcehead'] ) { $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 script aggregation for types whitelisted in should_aggregate-function. $should_aggregate = $this->should_aggregate($tag); if ( ! $should_aggregate ) { $tag = ''; continue; } if ( preg_match( '#]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) { // non-inline script. if ( $this->isremovable($tag, $this->jsremovables) ) { $this->content = str_replace( $tag, '', $this->content ); continue; } $origTag = null; $url = current( explode( '?', $source[2], 2 ) ); $path = $this->getpath($url); if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable($tag) ) { // ok to optimize, add to array. $this->scripts[] = $path; } else { $origTag = $tag; $newTag = $tag; // non-mergeable script (excluded or dynamic or external). if ( is_array( $excludeJS ) ) { // should we add flags? foreach ( $excludeJS as $exclTag => $exclFlags) { if ( false !== strpos( $origTag, $exclTag ) && in_array( $exclFlags, array( 'async', 'defer' ) ) ) { $newTag = str_replace( '#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? $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag ); if ( $this->ismovable($tag) || '' !== $autoptimize_js_moveable ) { if ( $this->movetolast($tag) || 'last' === $autoptimize_js_moveable ) { $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; } /** * Determines wheter a certain `'; $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 ( ! $this->aggregate ) { return false; } if ( ! empty( $this->whitelist ) ) { foreach ( $this->whitelist as $match ) { if (false !== strpos( $tag, $match ) ) { return true; } } // no match with whitelist return false; } else { foreach($this->domove as $match) { if ( false !== strpos( $tag, $match ) ) { // Matched something return false; } } if ( $this->movetolast($tag) ) { return false; } foreach( $this->dontmove as $match ) { if ( false !== strpos( $tag, $match ) ) { // Matched something return false; } } // If we're here it's safe to merge return true; } } // Checks agains the blacklist private function ismovable($tag) { if ( true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) { return false; } foreach ( $this->domove as $match ) { if ( false !== strpos( $tag, $match ) ) { // Matched something return true; } } if ( $this->movetolast($tag) ) { return true; } foreach ( $this->dontmove as $match ) { if ( false !== strpos( $tag, $match ) ) { // 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 ( false !== strpos( $tag, $match ) ) { // Matched, return true return true; } } // Should be in 'first' return false; } /** * Determines wheter a