1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
4
|
|
|
exit; |
5
|
|
|
} |
6
|
|
|
|
7
|
|
|
class autoptimizeScripts extends autoptimizeBase |
8
|
|
|
{ |
9
|
|
|
private $scripts = array(); |
10
|
|
|
private $move = array( |
11
|
|
|
'first' => array(), |
12
|
|
|
'last' => array() |
13
|
|
|
); |
14
|
|
|
|
15
|
|
|
private $dontmove = array( |
16
|
|
|
'document.write','html5.js','show_ads.js','google_ad','histats.com/js','statcounter.com/counter/counter.js', |
17
|
|
|
'ws.amazon.com/widgets','media.fastclick.net','/ads/','comment-form-quicktags/quicktags.php','edToolbar', |
18
|
|
|
'intensedebate.com','scripts.chitika.net/','_gaq.push','jotform.com/','admin-bar.min.js','GoogleAnalyticsObject', |
19
|
|
|
'plupload.full.min.js','syntaxhighlighter','adsbygoogle','gist.github.com','_stq','nonce','post_id','data-noptimize' |
20
|
|
|
,'logHuman' |
21
|
|
|
); |
22
|
|
|
private $domove = array( |
23
|
|
|
'gaJsHost','load_cmc','jd.gallery.transitions.js','swfobject.embedSWF(','tiny_mce.js','tinyMCEPreInit.go' |
24
|
|
|
); |
25
|
|
|
private $domovelast = array( |
26
|
|
|
'addthis.com','/afsonline/show_afs_search.js','disqus.js','networkedblogs.com/getnetworkwidget','infolinks.com/js/', |
27
|
|
|
'jd.gallery.js.php','jd.gallery.transitions.js','swfobject.embedSWF(','linkwithin.com/widget.js','tiny_mce.js','tinyMCEPreInit.go' |
28
|
|
|
); |
29
|
|
|
public $cdn_url = ''; |
30
|
|
|
|
31
|
|
|
private $aggregate = true; |
32
|
|
|
private $trycatch = false; |
33
|
|
|
private $alreadyminified = false; |
34
|
|
|
private $forcehead = true; |
35
|
|
|
private $include_inline = false; |
36
|
|
|
private $jscode = ''; |
37
|
|
|
private $url = ''; |
38
|
|
|
private $restofcontent = ''; |
39
|
|
|
private $md5hash = ''; |
40
|
|
|
private $whitelist = ''; |
41
|
|
|
private $jsremovables = array(); |
42
|
|
|
private $inject_min_late = ''; |
43
|
|
|
private $minify_excluded = true; |
44
|
|
|
|
45
|
|
|
// Reads the page and collects script tags. |
46
|
|
|
public function read( $options ) |
47
|
|
|
{ |
48
|
|
|
$noptimizeJS = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content ); |
49
|
|
|
if ( $noptimizeJS ) { |
50
|
|
|
return false; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
// only optimize known good JS? |
54
|
|
|
$whitelistJS = apply_filters( 'autoptimize_filter_js_whitelist', '', $this->content ); |
55
|
|
View Code Duplication |
if ( ! empty( $whitelistJS ) ) { |
|
|
|
|
56
|
|
|
$this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelistJS ) ) ); |
|
|
|
|
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
// is there JS we should simply remove? |
60
|
|
|
$removableJS = apply_filters( 'autoptimize_filter_js_removables', '', $this->content ); |
61
|
|
View Code Duplication |
if ( !empty( $removableJS ) ) { |
|
|
|
|
62
|
|
|
$this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removableJS ) ) ); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
// only header? |
66
|
|
View Code Duplication |
if ( apply_filters( 'autoptimize_filter_js_justhead', $options['justhead'] ) ) { |
|
|
|
|
67
|
|
|
$content = explode( '</head>', $this->content, 2 ); |
68
|
|
|
$this->content = $content[0] . '</head>'; |
69
|
|
|
$this->restofcontent = $content[1]; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
// Determine whether we're doing JS-files aggregation or not. |
73
|
|
|
if ( ! $options['aggregate'] ) { |
74
|
|
|
$this->aggregate = false; |
75
|
|
|
} |
76
|
|
|
// Returning true for "dontaggregate" turns off aggregation. |
77
|
|
|
if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) { |
78
|
|
|
$this->aggregate = false; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
// include inline? |
82
|
|
|
if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) { |
83
|
|
|
$this->include_inline = true; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
// filter to "late inject minified JS", default to true for now (it is faster). |
87
|
|
|
$this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true ); |
88
|
|
|
|
89
|
|
|
// filters to override hardcoded do(nt)move(last) array contents (array in, array out!). |
90
|
|
|
$this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove ); |
91
|
|
|
$this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast ); |
92
|
|
|
$this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove ); |
93
|
|
|
|
94
|
|
|
// Determine whether excluded files should be minified if not yet so. |
95
|
|
|
if ( ! $options['minify_excluded'] && $options['aggregate'] ) { |
96
|
|
|
$this->minify_excluded = false; |
97
|
|
|
} |
98
|
|
|
$this->minify_excluded = apply_filters( 'autoptimize_filter_js_minify_excluded', $this->minify_excluded ); |
99
|
|
|
|
100
|
|
|
// get extra exclusions settings or filter. |
101
|
|
|
$excludeJS = $options['js_exclude']; |
102
|
|
|
$excludeJS = apply_filters( 'autoptimize_filter_js_exclude', $excludeJS, $this->content ); |
103
|
|
|
|
104
|
|
|
if ( '' !== $excludeJS ) { |
105
|
|
|
if ( is_array( $excludeJS ) ) { |
106
|
|
|
if ( ( $removeKeys = array_keys( $excludeJS, 'remove' ) ) !== false ) { |
107
|
|
|
foreach ( $removeKeys as $removeKey ) { |
108
|
|
|
unset( $excludeJS[$removeKey] ); |
109
|
|
|
$this->jsremovables[] = $removeKey; |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
$exclJSArr = array_keys( $excludeJS ); |
113
|
|
|
} else { |
114
|
|
|
$exclJSArr = array_filter( array_map( 'trim', explode( ',', $excludeJS ) ) ); |
115
|
|
|
} |
116
|
|
|
$this->dontmove = array_merge( $exclJSArr, $this->dontmove ); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
// Should we add try-catch? |
120
|
|
|
if ( $options['trycatch'] ) { |
121
|
|
|
$this->trycatch = true; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
// force js in head? |
125
|
|
|
if ( $options['forcehead'] ) { |
126
|
|
|
$this->forcehead = true; |
127
|
|
|
} else { |
128
|
|
|
$this->forcehead = false; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
$this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead ); |
132
|
|
|
|
133
|
|
|
// get cdn url. |
134
|
|
|
$this->cdn_url = $options['cdn_url']; |
135
|
|
|
|
136
|
|
|
// noptimize me. |
137
|
|
|
$this->content = $this->hide_noptimize($this->content); |
138
|
|
|
|
139
|
|
|
// Save IE hacks. |
140
|
|
|
$this->content = $this->hide_iehacks($this->content); |
141
|
|
|
|
142
|
|
|
// comments. |
143
|
|
|
$this->content = $this->hide_comments($this->content); |
144
|
|
|
|
145
|
|
|
// Get script files. |
146
|
|
|
if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) { |
147
|
|
|
foreach( $matches[0] as $tag ) { |
148
|
|
|
// only consider script aggregation for types whitelisted in should_aggregate-function. |
149
|
|
|
$should_aggregate = $this->should_aggregate($tag); |
150
|
|
|
if ( ! $should_aggregate ) { |
151
|
|
|
$tag = ''; |
152
|
|
|
continue; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) { |
156
|
|
|
// non-inline script. |
157
|
|
View Code Duplication |
if ( $this->isremovable($tag, $this->jsremovables) ) { |
|
|
|
|
158
|
|
|
$this->content = str_replace( $tag, '', $this->content ); |
159
|
|
|
continue; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
$origTag = null; |
|
|
|
|
163
|
|
|
$url = current( explode( '?', $source[2], 2 ) ); |
164
|
|
|
$path = $this->getpath($url); |
165
|
|
|
if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable($tag) ) { |
166
|
|
|
// ok to optimize, add to array. |
167
|
|
|
$this->scripts[] = $path; |
168
|
|
|
} else { |
169
|
|
|
$origTag = $tag; |
170
|
|
|
$newTag = $tag; |
171
|
|
|
|
172
|
|
|
// non-mergeable script (excluded or dynamic or external). |
173
|
|
|
if ( is_array( $excludeJS ) ) { |
174
|
|
|
// should we add flags? |
175
|
|
|
foreach ( $excludeJS as $exclTag => $exclFlags) { |
176
|
|
|
if ( false !== strpos( $origTag, $exclTag ) && in_array( $exclFlags, array( 'async', 'defer' ) ) ) { |
177
|
|
|
$newTag = str_replace( '<script ', '<script ' . $exclFlags . ' ', $newTag ); |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
// Should we minify the non-aggregated script? |
183
|
|
|
// -> if aggregate is on and exclude minify is on |
184
|
|
|
// -> if aggregate is off and the file is not in dontmove. |
185
|
|
View Code Duplication |
if ( $path && $this->minify_excluded ) { |
|
|
|
|
186
|
|
|
$consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false ); |
187
|
|
|
if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) { |
188
|
|
|
$minified_url = $this->minify_single( $path ); |
189
|
|
|
// replace orig URL with minified URL from cache if so. |
190
|
|
|
if ( ! empty( $minified_url ) ) { |
191
|
|
|
$newTag = str_replace( $url, $minified_url, $newTag ); |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
if ( $this->ismovable($newTag) ) { |
197
|
|
|
// can be moved, flags and all. |
198
|
|
|
if ( $this->movetolast($newTag) ) { |
199
|
|
|
$this->move['last'][] = $newTag; |
200
|
|
|
} else { |
201
|
|
|
$this->move['first'][] = $newTag; |
202
|
|
|
} |
203
|
|
|
} else { |
204
|
|
|
// cannot be moved, so if flag was added re-inject altered tag immediately. |
205
|
|
|
if ( $origTag !== $newTag ) { |
206
|
|
|
$this->content = str_replace( $origTag, $newTag, $this->content ); |
207
|
|
|
$origTag = ''; |
|
|
|
|
208
|
|
|
} |
209
|
|
|
// and forget about the $tag (not to be touched any more). |
210
|
|
|
$tag = ''; |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
} else { |
214
|
|
|
// Inline script. |
215
|
|
View Code Duplication |
if ( $this->isremovable($tag, $this->jsremovables) ) { |
|
|
|
|
216
|
|
|
$this->content = str_replace( $tag, '', $this->content ); |
217
|
|
|
continue; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
// unhide comments, as javascript may be wrapped in comment-tags for old times' sake. |
221
|
|
|
$tag = $this->restore_comments($tag); |
222
|
|
|
if ( $this->ismergeable($tag) && $this->include_inline ) { |
223
|
|
|
preg_match( '#<script.*>(.*)</script>#Usmi', $tag , $code ); |
224
|
|
|
$code = preg_replace('#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] ); |
225
|
|
|
$code = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code ); |
226
|
|
|
$this->scripts[] = 'INLINE;' . $code; |
227
|
|
|
} else { |
228
|
|
|
// Can we move this? |
229
|
|
|
$autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag ); |
230
|
|
|
if ( $this->ismovable($tag) || '' !== $autoptimize_js_moveable ) { |
231
|
|
|
if ( $this->movetolast($tag) || 'last' === $autoptimize_js_moveable ) { |
232
|
|
|
$this->move['last'][] = $tag; |
233
|
|
|
} else { |
234
|
|
|
$this->move['first'][] = $tag; |
235
|
|
|
} |
236
|
|
|
} else { |
237
|
|
|
// We shouldn't touch this. |
238
|
|
|
$tag = ''; |
239
|
|
|
} |
240
|
|
|
} |
241
|
|
|
// Re-hide comments to be able to do the removal based on tag from $this->content. |
242
|
|
|
$tag = $this->hide_comments($tag); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
//Remove the original script tag. |
246
|
|
|
$this->content = str_replace( $tag, '', $this->content ); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
return true; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
// No script files, great ;-) |
253
|
|
|
return false; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Determines wheter a certain `<script>` $tag should be aggregated or not. |
258
|
|
|
* |
259
|
|
|
* We consider these as "aggregation-safe" currently: |
260
|
|
|
* - script tags without a `type` attribute |
261
|
|
|
* - script tags with these `type` attribute values: `text/javascript`, `text/ecmascript`, `application/javascript`, |
262
|
|
|
* and `application/ecmascript` |
263
|
|
|
* |
264
|
|
|
* Everything else should return false. |
265
|
|
|
* |
266
|
|
|
* @link https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type |
267
|
|
|
* |
268
|
|
|
* @param string $tag |
269
|
|
|
* @return bool |
270
|
|
|
*/ |
271
|
|
|
public function should_aggregate($tag) |
272
|
|
|
{ |
273
|
|
|
// We're only interested in the type attribute of the <script> tag itself, not any possible |
274
|
|
|
// inline code that might just contain the 'type=' string... |
275
|
|
|
$tag_parts = array(); |
276
|
|
|
preg_match( '#<(script[^>]*)>#i', $tag, $tag_parts); |
277
|
|
|
$tag_without_contents = null; |
278
|
|
|
if ( ! empty( $tag_parts[1] ) ) { |
279
|
|
|
$tag_without_contents = $tag_parts[1]; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
$has_type = ( strpos( $tag_without_contents, 'type' ) !== false ); |
283
|
|
|
|
284
|
|
|
$type_valid = false; |
285
|
|
|
if ( $has_type ) { |
286
|
|
|
$type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents ); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
$should_aggregate = false; |
290
|
|
|
if ( ! $has_type || $type_valid ) { |
291
|
|
|
$should_aggregate = true; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
return $should_aggregate; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
//Joins and optimizes JS |
298
|
|
|
public function minify() |
299
|
|
|
{ |
300
|
|
|
foreach ( $this->scripts as $script ) { |
301
|
|
|
// TODO/FIXME: some duplicate code here, can be reduced/simplified |
302
|
|
|
if ( preg_match( '#^INLINE;#', $script ) ) { |
303
|
|
|
// Inline script |
304
|
|
|
$script = preg_replace( '#^INLINE;#', '', $script ); |
305
|
|
|
$script = rtrim( $script, ";\n\t\r" ) . ';'; |
306
|
|
|
// Add try-catch? |
307
|
|
|
if ( $this->trycatch ) { |
308
|
|
|
$script = 'try{' . $script . '}catch(e){}'; |
309
|
|
|
} |
310
|
|
|
$tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' ); |
311
|
|
|
if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscript ) ) { |
312
|
|
|
$script = $tmpscript; |
313
|
|
|
$this->alreadyminified = true; |
314
|
|
|
} |
315
|
|
|
$this->jscode .= "\n" . $script; |
316
|
|
|
} else { |
317
|
|
|
// External script |
318
|
|
|
if ( false !== $script && file_exists( $script ) && is_readable( $script ) ) { |
319
|
|
|
$scriptsrc = file_get_contents( $script ); |
320
|
|
|
$scriptsrc = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $scriptsrc ); |
321
|
|
|
$scriptsrc = rtrim( $scriptsrc, ";\n\t\r" ) . ';'; |
322
|
|
|
// Add try-catch? |
323
|
|
|
if ( $this->trycatch ) { |
324
|
|
|
$scriptsrc = 'try{' . $scriptsrc . '}catch(e){}'; |
325
|
|
|
} |
326
|
|
|
$tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script ); |
327
|
|
View Code Duplication |
if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscriptsrc ) ) { |
|
|
|
|
328
|
|
|
$scriptsrc = $tmpscriptsrc; |
329
|
|
|
$this->alreadyminified = true; |
330
|
|
|
} else if ( $this->can_inject_late($script) ) { |
331
|
|
|
$scriptsrc = self::build_injectlater_marker($script, md5($scriptsrc)); |
332
|
|
|
} |
333
|
|
|
$this->jscode .= "\n" . $scriptsrc; |
334
|
|
|
}/*else{ |
335
|
|
|
//Couldn't read JS. Maybe getpath isn't working? |
336
|
|
|
}*/ |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
// Check for already-minified code |
341
|
|
|
$this->md5hash = md5( $this->jscode ); |
342
|
|
|
$ccheck = new autoptimizeCache($this->md5hash, 'js'); |
343
|
|
|
if ( $ccheck->check() ) { |
344
|
|
|
$this->jscode = $ccheck->retrieve(); |
|
|
|
|
345
|
|
|
return true; |
346
|
|
|
} |
347
|
|
|
unset( $ccheck ); |
348
|
|
|
|
349
|
|
|
// $this->jscode has all the uncompressed code now. |
350
|
|
|
if ( true !== $this->alreadyminified ) { |
351
|
|
|
if ( apply_filters( 'autoptimize_js_do_minify', true ) ) { |
352
|
|
|
$tmp_jscode = trim( JSMin::minify( $this->jscode ) ); |
353
|
|
|
if ( ! empty( $tmp_jscode ) ) { |
354
|
|
|
$this->jscode = $tmp_jscode; |
355
|
|
|
unset( $tmp_jscode ); |
356
|
|
|
} |
357
|
|
|
$this->jscode = $this->inject_minified( $this->jscode ); |
358
|
|
|
$this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode ); |
359
|
|
|
return true; |
360
|
|
|
} else { |
361
|
|
|
$this->jscode = $this->inject_minified( $this->jscode ); |
362
|
|
|
return false; |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
$this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode ); |
367
|
|
|
return true; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
// Caches the JS in uncompressed, deflated and gzipped form. |
371
|
|
|
public function cache() |
372
|
|
|
{ |
373
|
|
|
$cache = new autoptimizeCache($this->md5hash, 'js'); |
374
|
|
|
if ( ! $cache->check() ) { |
375
|
|
|
// Cache our code |
376
|
|
|
$cache->cache($this->jscode, 'text/javascript'); |
377
|
|
|
} |
378
|
|
|
$this->url = AUTOPTIMIZE_CACHE_URL . $cache->getname(); |
379
|
|
|
$this->url = $this->url_replace_cdn($this->url); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
// Returns the content |
383
|
|
|
public function getcontent() |
384
|
|
|
{ |
385
|
|
|
// Restore the full content |
386
|
|
|
if ( ! empty( $this->restofcontent ) ) { |
387
|
|
|
$this->content .= $this->restofcontent; |
388
|
|
|
$this->restofcontent = ''; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
// Add the scripts taking forcehead/ deferred (default) into account |
392
|
|
|
if ( $this->forcehead ) { |
393
|
|
|
$replaceTag = array( '</head>', 'before' ); |
394
|
|
|
$defer = ''; |
395
|
|
|
} else { |
396
|
|
|
$replaceTag = array( '</body>', 'before' ); |
397
|
|
|
$defer = 'defer '; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
$defer = apply_filters( 'autoptimize_filter_js_defer', $defer ); |
401
|
|
|
$type_js = ''; |
402
|
|
|
if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) { |
403
|
|
|
$type_js = 'type="text/javascript" '; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
$bodyreplacementpayload = '<script ' . $type_js . $defer . 'src="' . $this->url . '"></script>'; |
407
|
|
|
$bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload ); |
408
|
|
|
|
409
|
|
|
$bodyreplacement = implode( '', $this->move['first'] ); |
410
|
|
|
$bodyreplacement .= $bodyreplacementpayload; |
411
|
|
|
$bodyreplacement .= implode( '', $this->move['last'] ); |
412
|
|
|
|
413
|
|
|
$replaceTag = apply_filters( 'autoptimize_filter_js_replacetag', $replaceTag ); |
414
|
|
|
|
415
|
|
|
if ( strlen( $this->jscode ) > 0 ) { |
416
|
|
|
$this->inject_in_html( $bodyreplacement, $replaceTag ); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
// Restore comments. |
420
|
|
|
$this->content = $this->restore_comments( $this->content ); |
421
|
|
|
|
422
|
|
|
// Restore IE hacks. |
423
|
|
|
$this->content = $this->restore_iehacks( $this->content ); |
424
|
|
|
|
425
|
|
|
// Restore noptimize. |
426
|
|
|
$this->content = $this->restore_noptimize( $this->content ); |
427
|
|
|
|
428
|
|
|
// Return the modified HTML. |
429
|
|
|
return $this->content; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
// Checks against the white- and blacklists |
433
|
|
|
private function ismergeable($tag) |
434
|
|
|
{ |
435
|
|
|
if ( ! $this->aggregate ) { |
436
|
|
|
return false; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
if ( ! empty( $this->whitelist ) ) { |
440
|
|
|
foreach ( $this->whitelist as $match ) { |
|
|
|
|
441
|
|
|
if (false !== strpos( $tag, $match ) ) { |
442
|
|
|
return true; |
443
|
|
|
} |
444
|
|
|
} |
445
|
|
|
// no match with whitelist |
446
|
|
|
return false; |
447
|
|
|
} else { |
448
|
|
|
foreach($this->domove as $match) { |
449
|
|
|
if ( false !== strpos( $tag, $match ) ) { |
450
|
|
|
// Matched something |
451
|
|
|
return false; |
452
|
|
|
} |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
if ( $this->movetolast($tag) ) { |
456
|
|
|
return false; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
foreach( $this->dontmove as $match ) { |
460
|
|
|
if ( false !== strpos( $tag, $match ) ) { |
461
|
|
|
// Matched something |
462
|
|
|
return false; |
463
|
|
|
} |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
// If we're here it's safe to merge |
467
|
|
|
return true; |
468
|
|
|
} |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
// Checks agains the blacklist |
472
|
|
|
private function ismovable($tag) |
473
|
|
|
{ |
474
|
|
|
if ( true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) { |
475
|
|
|
return false; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
foreach ( $this->domove as $match ) { |
479
|
|
|
if ( false !== strpos( $tag, $match ) ) { |
480
|
|
|
// Matched something |
481
|
|
|
return true; |
482
|
|
|
} |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
if ( $this->movetolast($tag) ) { |
486
|
|
|
return true; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
foreach ( $this->dontmove as $match ) { |
490
|
|
|
if ( false !== strpos( $tag, $match ) ) { |
491
|
|
|
// Matched something |
492
|
|
|
return false; |
493
|
|
|
} |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
// If we're here it's safe to move |
497
|
|
|
return true; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
private function movetolast($tag) |
501
|
|
|
{ |
502
|
|
|
foreach ( $this->domovelast as $match ) { |
503
|
|
|
if ( false !== strpos( $tag, $match ) ) { |
504
|
|
|
// Matched, return true |
505
|
|
|
return true; |
506
|
|
|
} |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
// Should be in 'first' |
510
|
|
|
return false; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Determines wheter a <script> $tag can be excluded from minification (as already minified) based on: |
515
|
|
|
* - inject_min_late being active |
516
|
|
|
* - filename ending in `min.js` |
517
|
|
|
* - filename matching `js/jquery/jquery.js` (wordpress core jquery, is minified) |
518
|
|
|
* - filename matching one passed in the consider minified filter |
519
|
|
|
* |
520
|
|
|
* @param string $jsPath |
521
|
|
|
* @return bool |
522
|
|
|
*/ |
523
|
|
|
private function can_inject_late($jsPath) { |
524
|
|
|
$consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false ); |
525
|
|
|
if ( true !== $this->inject_min_late ) { |
526
|
|
|
// late-inject turned off |
527
|
|
|
return false; |
528
|
|
|
} else if ( ( false === strpos( $jsPath, 'min.js' ) ) && ( false === strpos( $jsPath, 'wp-includes/js/jquery/jquery.js' ) ) && ( str_replace( $consider_minified_array, '', $jsPath ) === $jsPath ) ) { |
529
|
|
|
// file not minified based on filename & filter |
530
|
|
|
return false; |
531
|
|
|
} else { |
532
|
|
|
// phew, all is safe, we can late-inject |
533
|
|
|
return true; |
534
|
|
|
} |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Returns whether we're doing aggregation or not. |
539
|
|
|
* |
540
|
|
|
* @return bool |
541
|
|
|
*/ |
542
|
|
|
public function aggregating() |
543
|
|
|
{ |
544
|
|
|
return $this->aggregate; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Minifies a single local js file and returns its (cached) url. |
549
|
|
|
* |
550
|
|
|
* @param string $filepath Filepath. |
551
|
|
|
* @param bool $cache_miss Optional. Force a cache miss. Default false. |
552
|
|
|
* |
553
|
|
|
* @return bool|string Url pointing to the minified js file or false. |
554
|
|
|
*/ |
555
|
|
|
public function minify_single( $filepath, $cache_miss = false ) |
556
|
|
|
{ |
557
|
|
|
$contents = $this->prepare_minify_single( $filepath ); |
558
|
|
|
|
559
|
|
|
if ( empty( $contents ) ) { |
560
|
|
|
return false; |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
// Check cache. |
564
|
|
|
$hash = 'single_' . md5( $contents ); |
565
|
|
|
$cache = new autoptimizeCache( $hash, 'js' ); |
566
|
|
|
|
567
|
|
|
// If not in cache already, minify... |
568
|
|
|
if ( ! $cache->check() || $cache_miss ) { |
569
|
|
|
$contents = trim( JSMin::minify( $contents ) ); |
570
|
|
|
// Store in cache. |
571
|
|
|
$cache->cache( $contents, 'text/javascript' ); |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
$url = $this->build_minify_single_url( $cache ); |
575
|
|
|
|
576
|
|
|
return $url; |
577
|
|
|
} |
578
|
|
|
} |
579
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.