PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.3.1
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.3.1
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / matthiasmullie / minify / src / JS.php

JS.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.3.1, at vendor/matthiasmullie/minify/src/JS.php

622 lines 39.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * JavaScript minifier.
5 *
6 * Please report bugs on https://github.com/matthiasmullie/minify/issues
7 *
8 * @author Matthias Mullie <[email protected]>
9 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10 * @license MIT License
11 */
12
13 namespace MatthiasMullie\Minify;
14
15 /**
16 * JavaScript Minifier Class.
17 *
18 * Please report bugs on https://github.com/matthiasmullie/minify/issues
19 *
20 * @author Matthias Mullie <[email protected]>
21 * @author Tijs Verkoyen <[email protected]>
22 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
23 * @license MIT License
24 */
25 class JS extends Minify
26 {
27 /**
28 * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
29 *
30 * Note that regular expressions using that bit must have the PCRE_UTF8
31 * pattern modifier (/u) set.
32 *
33 * @internal
34 *
35 * @var string
36 */
37 const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}][$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}\x{ab09}-\x{ab0e}\x{ab11}-\x{ab16}\x{ab20}-\x{ab26}\x{ab28}-\x{ab2e}\x{abc0}-\x{abe2}\x{ac00}-\x{d7a3}\x{d7b0}-\x{d7c6}\x{d7cb}-\x{d7fb}\x{f900}-\x{fa6d}\x{fa70}-\x{fad9}\x{fb00}-\x{fb06}\x{fb13}-\x{fb17}\x{fb1d}\x{fb1f}-\x{fb28}\x{fb2a}-\x{fb36}\x{fb38}-\x{fb3c}\x{fb3e}\x{fb40}\x{fb41}\x{fb43}\x{fb44}\x{fb46}-\x{fbb1}\x{fbd3}-\x{fd3d}\x{fd50}-\x{fd8f}\x{fd92}-\x{fdc7}\x{fdf0}-\x{fdfb}\x{fe70}-\x{fe74}\x{fe76}-\x{fefc}\x{ff21}-\x{ff3a}\x{ff41}-\x{ff5a}\x{ff66}-\x{ffbe}\x{ffc2}-\x{ffc7}\x{ffca}-\x{ffcf}\x{ffd2}-\x{ffd7}\x{ffda}-\x{ffdc}0-9\x{0300}-\x{036f}\x{0483}-\x{0487}\x{0591}-\x{05bd}\x{05bf}\x{05c1}\x{05c2}\x{05c4}\x{05c5}\x{05c7}\x{0610}-\x{061a}\x{064b}-\x{0669}\x{0670}\x{06d6}-\x{06dc}\x{06df}-\x{06e4}\x{06e7}\x{06e8}\x{06ea}-\x{06ed}\x{06f0}-\x{06f9}\x{0711}\x{0730}-\x{074a}\x{07a6}-\x{07b0}\x{07c0}-\x{07c9}\x{07eb}-\x{07f3}\x{0816}-\x{0819}\x{081b}-\x{0823}\x{0825}-\x{0827}\x{0829}-\x{082d}\x{0859}-\x{085b}\x{08e4}-\x{08fe}\x{0900}-\x{0903}\x{093a}-\x{093c}\x{093e}-\x{094f}\x{0951}-\x{0957}\x{0962}\x{0963}\x{0966}-\x{096f}\x{0981}-\x{0983}\x{09bc}\x{09be}-\x{09c4}\x{09c7}\x{09c8}\x{09cb}-\x{09cd}\x{09d7}\x{09e2}\x{09e3}\x{09e6}-\x{09ef}\x{0a01}-\x{0a03}\x{0a3c}\x{0a3e}-\x{0a42}\x{0a47}\x{0a48}\x{0a4b}-\x{0a4d}\x{0a51}\x{0a66}-\x{0a71}\x{0a75}\x{0a81}-\x{0a83}\x{0abc}\x{0abe}-\x{0ac5}\x{0ac7}-\x{0ac9}\x{0acb}-\x{0acd}\x{0ae2}\x{0ae3}\x{0ae6}-\x{0aef}\x{0b01}-\x{0b03}\x{0b3c}\x{0b3e}-\x{0b44}\x{0b47}\x{0b48}\x{0b4b}-\x{0b4d}\x{0b56}\x{0b57}\x{0b62}\x{0b63}\x{0b66}-\x{0b6f}\x{0b82}\x{0bbe}-\x{0bc2}\x{0bc6}-\x{0bc8}\x{0bca}-\x{0bcd}\x{0bd7}\x{0be6}-\x{0bef}\x{0c01}-\x{0c03}\x{0c3e}-\x{0c44}\x{0c46}-\x{0c48}\x{0c4a}-\x{0c4d}\x{0c55}\x{0c56}\x{0c62}\x{0c63}\x{0c66}-\x{0c6f}\x{0c82}\x{0c83}\x{0cbc}\x{0cbe}-\x{0cc4}\x{0cc6}-\x{0cc8}\x{0cca}-\x{0ccd}\x{0cd5}\x{0cd6}\x{0ce2}\x{0ce3}\x{0ce6}-\x{0cef}\x{0d02}\x{0d03}\x{0d3e}-\x{0d44}\x{0d46}-\x{0d48}\x{0d4a}-\x{0d4d}\x{0d57}\x{0d62}\x{0d63}\x{0d66}-\x{0d6f}\x{0d82}\x{0d83}\x{0dca}\x{0dcf}-\x{0dd4}\x{0dd6}\x{0dd8}-\x{0ddf}\x{0df2}\x{0df3}\x{0e31}\x{0e34}-\x{0e3a}\x{0e47}-\x{0e4e}\x{0e50}-\x{0e59}\x{0eb1}\x{0eb4}-\x{0eb9}\x{0ebb}\x{0ebc}\x{0ec8}-\x{0ecd}\x{0ed0}-\x{0ed9}\x{0f18}\x{0f19}\x{0f20}-\x{0f29}\x{0f35}\x{0f37}\x{0f39}\x{0f3e}\x{0f3f}\x{0f71}-\x{0f84}\x{0f86}\x{0f87}\x{0f8d}-\x{0f97}\x{0f99}-\x{0fbc}\x{0fc6}\x{102b}-\x{103e}\x{1040}-\x{1049}\x{1056}-\x{1059}\x{105e}-\x{1060}\x{1062}-\x{1064}\x{1067}-\x{106d}\x{1071}-\x{1074}\x{1082}-\x{108d}\x{108f}-\x{109d}\x{135d}-\x{135f}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17d3}\x{17dd}\x{17e0}-\x{17e9}\x{180b}-\x{180d}\x{1810}-\x{1819}\x{18a9}\x{1920}-\x{192b}\x{1930}-\x{193b}\x{1946}-\x{194f}\x{19b0}-\x{19c0}\x{19c8}\x{19c9}\x{19d0}-\x{19d9}\x{1a17}-\x{1a1b}\x{1a55}-\x{1a5e}\x{1a60}-\x{1a7c}\x{1a7f}-\x{1a89}\x{1a90}-\x{1a99}\x{1b00}-\x{1b04}\x{1b34}-\x{1b44}\x{1b50}-\x{1b59}\x{1b6b}-\x{1b73}\x{1b80}-\x{1b82}\x{1ba1}-\x{1bad}\x{1bb0}-\x{1bb9}\x{1be6}-\x{1bf3}\x{1c24}-\x{1c37}\x{1c40}-\x{1c49}\x{1c50}-\x{1c59}\x{1cd0}-\x{1cd2}\x{1cd4}-\x{1ce8}\x{1ced}\x{1cf2}-\x{1cf4}\x{1dc0}-\x{1de6}\x{1dfc}-\x{1dff}\x{200c}\x{200d}\x{203f}\x{2040}\x{2054}\x{20d0}-\x{20dc}\x{20e1}\x{20e5}-\x{20f0}\x{2cef}-\x{2cf1}\x{2d7f}\x{2de0}-\x{2dff}\x{302a}-\x{302f}\x{3099}\x{309a}\x{a620}-\x{a629}\x{a66f}\x{a674}-\x{a67d}\x{a69f}\x{a6f0}\x{a6f1}\x{a802}\x{a806}\x{a80b}\x{a823}-\x{a827}\x{a880}\x{a881}\x{a8b4}-\x{a8c4}\x{a8d0}-\x{a8d9}\x{a8e0}-\x{a8f1}\x{a900}-\x{a909}\x{a926}-\x{a92d}\x{a947}-\x{a953}\x{a980}-\x{a983}\x{a9b3}-\x{a9c0}\x{a9d0}-\x{a9d9}\x{aa29}-\x{aa36}\x{aa43}\x{aa4c}\x{aa4d}\x{aa50}-\x{aa59}\x{aa7b}\x{aab0}\x{aab2}-\x{aab4}\x{aab7}\x{aab8}\x{aabe}\x{aabf}\x{aac1}\x{aaeb}-\x{aaef}\x{aaf5}\x{aaf6}\x{abe3}-\x{abea}\x{abec}\x{abed}\x{abf0}-\x{abf9}\x{fb1e}\x{fe00}-\x{fe0f}\x{fe20}-\x{fe26}\x{fe33}\x{fe34}\x{fe4d}-\x{fe4f}\x{ff10}-\x{ff19}\x{ff3f}]*\b';
38
39 /**
40 * Full list of JavaScript reserved words.
41 * Will be loaded from /data/js/keywords_reserved.txt.
42 *
43 * @see https://mathiasbynens.be/notes/reserved-keywords
44 *
45 * @var string[]
46 */
47 protected $keywordsReserved = array();
48
49 /**
50 * List of JavaScript reserved words that accept a <variable, value, ...>
51 * after them. Some end of lines are not the end of a statement, like with
52 * these keywords.
53 *
54 * E.g.: we shouldn't insert a ; after this else
55 * else
56 * console.log('this is quite fine')
57 *
58 * Will be loaded from /data/js/keywords_before.txt
59 *
60 * @var string[]
61 */
62 protected $keywordsBefore = array();
63
64 /**
65 * List of JavaScript reserved words that accept a <variable, value, ...>
66 * before them. Some end of lines are not the end of a statement, like when
67 * continued by one of these keywords on the newline.
68 *
69 * E.g.: we shouldn't insert a ; before this instanceof
70 * variable
71 * instanceof String
72 *
73 * Will be loaded from /data/js/keywords_after.txt
74 *
75 * @var string[]
76 */
77 protected $keywordsAfter = array();
78
79 /**
80 * List of all JavaScript operators.
81 *
82 * Will be loaded from /data/js/operators.txt
83 *
84 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
85 *
86 * @var string[]
87 */
88 protected $operators = array();
89
90 /**
91 * List of JavaScript operators that accept a <variable, value, ...> after
92 * them. Some end of lines are not the end of a statement, like with these
93 * operators.
94 *
95 * Note: Most operators are fine, we've only removed ++ and --.
96 * ++ & -- have to be joined with the value they're in-/decrementing.
97 *
98 * Will be loaded from /data/js/operators_before.txt
99 *
100 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
101 *
102 * @var string[]
103 */
104 protected $operatorsBefore = array();
105
106 /**
107 * List of JavaScript operators that accept a <variable, value, ...> before
108 * them. Some end of lines are not the end of a statement, like when
109 * continued by one of these operators on the newline.
110 *
111 * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
112 * There can't be a newline separating ! or ~ and whatever it is negating.
113 * ++ & -- have to be joined with the value they're in-/decrementing.
114 * ) & ] are "special" in that they have lots or usecases. () for example
115 * is used for function calls, for grouping, in if () and for (), ...
116 *
117 * Will be loaded from /data/js/operators_after.txt
118 *
119 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
120 *
121 * @var string[]
122 */
123 protected $operatorsAfter = array();
124
125 /**
126 * {@inheritdoc}
127 */
128 public function __construct()
129 {
130 call_user_func_array(array('\\MatthiasMullie\Minify\\Minify', '__construct'), func_get_args());
131
132 $dataDir = __DIR__ . '/../data/js/';
133 $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
134 $this->keywordsReserved = file($dataDir . 'keywords_reserved.txt', $options);
135 $this->keywordsBefore = file($dataDir . 'keywords_before.txt', $options);
136 $this->keywordsAfter = file($dataDir . 'keywords_after.txt', $options);
137 $this->operators = file($dataDir . 'operators.txt', $options);
138 $this->operatorsBefore = file($dataDir . 'operators_before.txt', $options);
139 $this->operatorsAfter = file($dataDir . 'operators_after.txt', $options);
140 }
141
142 /**
143 * Minify the data.
144 * Perform JS optimizations.
145 *
146 * @param string[optional] $path Path to write the data to
147 *
148 * @return string The minified data
149 */
150 public function execute($path = null)
151 {
152 $content = '';
153
154 /*
155 * Let's first take out strings, comments and regular expressions.
156 * All of these can contain JS code-like characters, and we should make
157 * sure any further magic ignores anything inside of these.
158 *
159 * Consider this example, where we should not strip any whitespace:
160 * var str = "a test";
161 *
162 * Comments will be removed altogether, strings and regular expressions
163 * will be replaced by placeholder text, which we'll restore later.
164 */
165 $this->extractStrings('\'"`');
166 $this->stripComments();
167 $this->extractRegex();
168
169 // loop files
170 foreach ($this->data as $source => $js) {
171 // take out strings, comments & regex (for which we've registered
172 // the regexes just a few lines earlier)
173 $js = $this->replace($js);
174
175 $js = $this->propertyNotation($js);
176 $js = $this->shortenBools($js);
177 $js = $this->stripWhitespace($js);
178
179 // combine js: separating the scripts by a ;
180 $content .= $js . ';';
181 }
182
183 // clean up leftover `;`s from the combination of multiple scripts
184 $content = ltrim($content, ';');
185 $content = (string) substr($content, 0, -1);
186
187 /*
188 * Earlier, we extracted strings & regular expressions and replaced them
189 * with placeholder text. This will restore them.
190 */
191 $content = $this->restoreExtractedData($content);
192
193 return $content;
194 }
195
196 /**
197 * Strip comments from source code.
198 */
199 protected function stripComments()
200 {
201 $this->stripMultilineComments();
202
203 // single-line comments
204 $this->registerPattern('/\/\/.*$/m', '');
205 }
206
207 /**
208 * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
209 *
210 * The content inside the regex can contain characters that may be confused
211 * for JS code: e.g. it could contain whitespace it needs to match & we
212 * don't want to strip whitespace in there.
213 *
214 * The regex can be pretty simple: we don't have to care about comments,
215 * (which also use slashes) because stripComments() will have stripped those
216 * already.
217 *
218 * This method will replace all string content with simple REGEX#
219 * placeholder text, so we've rid all regular expressions from characters
220 * that may be misinterpreted. Original regex content will be saved in
221 * $this->extracted and after doing all other minifying, we can restore the
222 * original content via restoreRegex()
223 */
224 protected function extractRegex()
225 {
226 // PHP only supports $this inside anonymous functions since 5.4
227 $minifier = $this;
228 $callback = function ($match) use ($minifier) {
229 $count = count($minifier->extracted);
230 $placeholder = '"' . $count . '"';
231 $minifier->extracted[$placeholder] = $match[0];
232
233 return $placeholder;
234 };
235
236 // match all chars except `/` and `\`
237 // `\` is allowed though, along with whatever char follows (which is the
238 // one being escaped)
239 // this should allow all chars, except for an unescaped `/` (= the one
240 // closing the regex)
241 // then also ignore bare `/` inside `[]`, where they don't need to be
242 // escaped: anything inside `[]` can be ignored safely
243 $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
244
245 // a regular expression can only be followed by a few operators or some
246 // of the RegExp methods (a `\` followed by a variable or value is
247 // likely part of a division, not a regex)
248 $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
249 $before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|' . implode('|', $keywords) . ')\s*';
250 $propertiesAndMethods = array(
251 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
252 'constructor',
253 'flags',
254 'global',
255 'ignoreCase',
256 'multiline',
257 'source',
258 'sticky',
259 'unicode',
260 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
261 'compile(',
262 'exec(',
263 'test(',
264 'toSource(',
265 'toString(',
266 );
267 $delimiters = array_fill(0, count($propertiesAndMethods), '/');
268 $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
269 $after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.(' . implode('|', $propertiesAndMethods) . ')))';
270 $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
271
272 // regular expressions following a `)` are rather annoying to detect...
273 // quite often, `/` after `)` is a division operator & if it happens to
274 // be followed by another one (or a comment), it is likely to be
275 // confused for a regular expression
276 // however, it's perfectly possible for a regex to follow a `)`: after
277 // a single-line `if()`, `while()`, ... statement, for example
278 // since, when they occur like that, they're always the start of a
279 // statement, there's only a limited amount of ways they can be useful:
280 // by calling the regex methods directly
281 // if a regex following `)` is not followed by `.<property or method>`,
282 // it's quite likely not a regex
283 $before = '\)\s*';
284 $after = '(?=\s*\.(' . implode('|', $propertiesAndMethods) . '))';
285 $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
286
287 // 1 more edge case: a regex can be followed by a lot more operators or
288 // keywords if there's a newline (ASI) in between, where the operator
289 // actually starts a new statement
290 // (https://github.com/matthiasmullie/minify/issues/56)
291 $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
292 $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
293 $after = '(?=\s*\n\s*(' . implode('|', $operators) . '))';
294 $this->registerPattern('/' . $pattern . $after . '/', $callback);
295 }
296
297 /**
298 * Strip whitespace.
299 *
300 * We won't strip *all* whitespace, but as much as possible. The thing that
301 * we'll preserve are newlines we're unsure about.
302 * JavaScript doesn't require statements to be terminated with a semicolon.
303 * It will automatically fix missing semicolons with ASI (automatic semi-
304 * colon insertion) at the end of line causing errors (without semicolon.)
305 *
306 * Because it's sometimes hard to tell if a newline is part of a statement
307 * that should be terminated or not, we'll just leave some of them alone.
308 *
309 * @param string $content The content to strip the whitespace for
310 *
311 * @return string
312 */
313 protected function stripWhitespace($content)
314 {
315 // uniform line endings, make them all line feed
316 $content = str_replace(array("\r\n", "\r"), "\n", $content);
317
318 // collapse all non-line feed whitespace into a single space
319 $content = preg_replace('/[^\S\n]+/', ' ', $content);
320
321 // strip leading & trailing whitespace
322 $content = str_replace(array(" \n", "\n "), "\n", $content);
323
324 // collapse consecutive line feeds into just 1
325 $content = preg_replace('/\n+/', "\n", $content);
326
327 $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
328 $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
329 $operators = $this->getOperatorsForRegex($this->operators, '/');
330 $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
331 $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
332
333 // strip whitespace that ends in (or next line begin with) an operator
334 // that allows statements to be broken up over multiple lines
335 unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
336 $content = preg_replace(
337 array(
338 '/(' . implode('|', $operatorsBefore) . ')\s+/',
339 '/\s+(' . implode('|', $operatorsAfter) . ')/',
340 ),
341 '\\1',
342 $content
343 );
344
345 // make sure + and - can't be mistaken for, or joined into ++ and --
346 $content = preg_replace(
347 array(
348 '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
349 '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
350 ),
351 '\\1',
352 $content
353 );
354
355 // collapse whitespace around reserved words into single space
356 $content = preg_replace('/(^|[;\}\s])\K(' . implode('|', $keywordsBefore) . ')\s+/', '\\2 ', $content);
357 $content = preg_replace('/\s+(' . implode('|', $keywordsAfter) . ')(?=([;\{\s]|$))/', ' \\1', $content);
358
359 /*
360 * We didn't strip whitespace after a couple of operators because they
361 * could be used in different contexts and we can't be sure it's ok to
362 * strip the newlines. However, we can safely strip any non-line feed
363 * whitespace that follows them.
364 */
365 $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
366 $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
367 $content = preg_replace('/(' . implode('|', $operatorsDiffBefore) . ')[^\S\n]+/', '\\1', $content);
368 $content = preg_replace('/[^\S\n]+(' . implode('|', $operatorsDiffAfter) . ')/', '\\1', $content);
369
370 /*
371 * Whitespace after `return` can be omitted in a few occasions
372 * (such as when followed by a string or regex)
373 * Same for whitespace in between `)` and `{`, or between `{` and some
374 * keywords.
375 */
376 $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
377 $content = preg_replace('/\)\s+\{/', '){', $content);
378 $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
379
380 /*
381 * Get rid of double semicolons, except where they can be used like:
382 * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
383 * I'll safeguard these double semicolons inside for-loops by
384 * temporarily replacing them with an invalid condition: they won't have
385 * a double semicolon and will be easy to spot to restore afterwards.
386 */
387 $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
388 $content = preg_replace('/;+/', ';', $content);
389 $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
390
391 /*
392 * Next, we'll be removing all semicolons where ASI kicks in.
393 * for-loops however, can have an empty body (ending in only a
394 * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
395 * Here, nothing happens during the loop; it's just used to keep
396 * increasing `i`. With that ; omitted, the next line would be expected
397 * to be the for-loop's body... Same goes for while loops.
398 * I'm going to double that semicolon (if any) so after the next line,
399 * which strips semicolons here & there, we're still left with this one.
400 * Note the special recursive construct in the three inner parts of the for:
401 * (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
402 * functions bodies, e.g.: i<arr.map(function(e){return e}).length.
403 * Also note that the construct is applied only once and multiplied
404 * for each part of the for, otherwise it risks a catastrophic backtracking.
405 * The limitation is that it will not allow closures in more than one
406 * of the three parts for a specific for() case.
407 * REGEX throwing catastrophic backtracking: $content = preg_replace('/(for\([^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*\));(\}|$)/s', '\\1;;\\8', $content);
408 */
409 $content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
410 $content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
411 $content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
412
413 $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
414
415 /*
416 * Do the same for the if's that don't have a body but are followed by ;}
417 */
418 $content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
419
420 /*
421 * Below will also keep `;` after a `do{}while();` along with `while();`
422 * While these could be stripped after do-while, detecting this
423 * distinction is cumbersome, so I'll play it safe and make sure `;`
424 * after any kind of `while` is kept.
425 */
426 $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
427
428 /*
429 * We also can't strip empty else-statements. Even though they're
430 * useless and probably shouldn't be in the code in the first place, we
431 * shouldn't be stripping the `;` that follows it as it breaks the code.
432 * We can just remove those useless else-statements completely.
433 *
434 * @see https://github.com/matthiasmullie/minify/issues/91
435 */
436 $content = preg_replace('/else;/s', '', $content);
437
438 /*
439 * We also don't really want to terminate statements followed by closing
440 * curly braces (which we've ignored completely up until now) or end-of-
441 * script: ASI will kick in here & we're all about minifying.
442 * Semicolons at beginning of the file don't make any sense either.
443 */
444 $content = preg_replace('/;(\}|$)/s', '\\1', $content);
445 $content = ltrim($content, ';');
446
447 // get rid of remaining whitespace af beginning/end
448 return trim($content);
449 }
450
451 /**
452 * We'll strip whitespace around certain operators with regular expressions.
453 * This will prepare the given array by escaping all characters.
454 *
455 * @param string[] $operators
456 * @param string $delimiter
457 *
458 * @return string[]
459 */
460 protected function getOperatorsForRegex(array $operators, $delimiter = '/')
461 {
462 // escape operators for use in regex
463 $delimiters = array_fill(0, count($operators), $delimiter);
464 $escaped = array_map('preg_quote', $operators, $delimiters);
465
466 $operators = array_combine($operators, $escaped);
467
468 // ignore + & - for now, they'll get special treatment
469 unset($operators['+'], $operators['-']);
470
471 // dot can not just immediately follow a number; it can be confused for
472 // decimal point, or calling a method on it, e.g. 42 .toString()
473 $operators['.'] = '(?<![0-9]\s)\.';
474
475 // don't confuse = with other assignment shortcuts (e.g. +=)
476 $chars = preg_quote('+-*\=<>%&|', $delimiter);
477 $operators['='] = '(?<![' . $chars . '])\=';
478
479 return $operators;
480 }
481
482 /**
483 * We'll strip whitespace around certain keywords with regular expressions.
484 * This will prepare the given array by escaping all characters.
485 *
486 * @param string[] $keywords
487 * @param string $delimiter
488 *
489 * @return string[]
490 */
491 protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
492 {
493 // escape keywords for use in regex
494 $delimiter = array_fill(0, count($keywords), $delimiter);
495 $escaped = array_map('preg_quote', $keywords, $delimiter);
496
497 // add word boundaries
498 array_walk($keywords, function ($value) {
499 return '\b' . $value . '\b';
500 });
501
502 $keywords = array_combine($keywords, $escaped);
503
504 return $keywords;
505 }
506
507 /**
508 * Replaces all occurrences of array['key'] by array.key.
509 *
510 * @param string $content
511 *
512 * @return string
513 */
514 protected function propertyNotation($content)
515 {
516 // PHP only supports $this inside anonymous functions since 5.4
517 $minifier = $this;
518 $keywords = $this->keywordsReserved;
519 $callback = function ($match) use ($minifier, $keywords) {
520 $property = trim($minifier->extracted[$match[1]], '\'"');
521
522 /*
523 * Check if the property is a reserved keyword. In this context (as
524 * property of an object literal/array) it shouldn't matter, but IE8
525 * freaks out with "Expected identifier".
526 */
527 if (in_array($property, $keywords)) {
528 return $match[0];
529 }
530
531 /*
532 * See if the property is in a variable-like format (e.g.
533 * array['key-here'] can't be replaced by array.key-here since '-'
534 * is not a valid character there.
535 */
536 if (!preg_match('/^' . $minifier::REGEX_VARIABLE . '$/u', $property)) {
537 return $match[0];
538 }
539
540 return '.' . $property;
541 };
542
543 /*
544 * Figure out if previous character is a variable name (of the array
545 * we want to use property notation on) - this is to make sure
546 * standalone ['value'] arrays aren't confused for keys-of-an-array.
547 * We can (and only have to) check the last character, because PHP's
548 * regex implementation doesn't allow unfixed-length look-behind
549 * assertions.
550 */
551 preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
552 $previousChar = $previousChar[1];
553
554 /*
555 * Make sure word preceding the ['value'] is not a keyword, e.g.
556 * return['x']. Because -again- PHP's regex implementation doesn't allow
557 * unfixed-length look-behind assertions, I'm just going to do a lot of
558 * separate look-behind assertions, one for each keyword.
559 */
560 $keywords = $this->getKeywordsForRegex($keywords);
561 $keywords = '(?<!' . implode(')(?<!', $keywords) . ')';
562
563 return preg_replace_callback('/(?<=' . $previousChar . '|\])' . $keywords . '\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
564 }
565
566 /**
567 * Replaces true & false by !0 and !1.
568 *
569 * @param string $content
570 *
571 * @return string
572 */
573 protected function shortenBools($content)
574 {
575 /*
576 * 'true' or 'false' could be used as property names (which may be
577 * followed by whitespace) - we must not replace those!
578 * Since PHP doesn't allow variable-length (to account for the
579 * whitespace) lookbehind assertions, I need to capture the leading
580 * character and check if it's a `.`
581 */
582 $callback = function ($match) {
583 if (trim($match[1]) === '.') {
584 return $match[0];
585 }
586
587 return $match[1] . ($match[2] === 'true' ? '!0' : '!1');
588 };
589 $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
590
591 // for(;;) is exactly the same as while(true), but shorter :)
592 $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
593
594 // now make sure we didn't turn any do ... while(true) into do ... for(;;)
595 preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
596
597 // go backward to make sure positional offsets aren't altered when $content changes
598 $dos = array_reverse($dos);
599 foreach ($dos as $do) {
600 $offsetDo = $do[0][1];
601
602 // find all `while` (now `for`) following `do`: one of those must be
603 // associated with the `do` and be turned back into `while`
604 preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
605 foreach ($whiles as $while) {
606 $offsetWhile = $while[0][1];
607
608 $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
609 $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
610 if ($open === $close) {
611 // only restore `while` if amount of `{` and `}` are the same;
612 // otherwise, that `for` isn't associated with this `do`
613 $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
614 break;
615 }
616 }
617 }
618
619 return $content;
620 }
621 }
622