PluginProbe ʕ •ᴥ•ʔ
10Web Booster – Website speed optimization, Cache & Page Speed optimizer / trunk
10Web Booster – Website speed optimization, Cache & Page Speed optimizer vtrunk
2.33.0 2.30.5 2.30.7 2.30.9 2.31.10 2.31.8 2.32.11 2.32.21 2.32.3 2.32.4 2.32.7 2.6.31 2.6.40 2.6.42 2.6.7 2.7.37 2.7.44 2.7.47 2.8.18 2.8.19 2.8.32 2.8.34 2.8.35 2.9.23 2.9.24 2.9.25 2.9.27 v2.27.4 trunk 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.17 2.0.18 2.0.21 2.0.22 2.0.25 2.0.26 2.0.27 2.0.3 2.0.7 2.0.9 2.10.46 2.10.65 2.10.66 2.10.68 2.11.41 2.11.42 2.11.43 2.12.15 2.12.21 2.12.22 2.12.23 2.12.26 2.13.37 2.13.40 2.13.41 2.13.42 2.13.44 2.13.45 2.13.47 2.14.49 2.14.50 2.15.18 2.17.21 2.17.23 2.18.17 2.19.44 2.19.45 2.19.46 2.19.49 2.2.12 2.2.15 2.2.16 2.2.18 2.2.8 2.20.31 2.20.32 2.20.33 2.21.11 2.21.12 2.21.16 2.21.25 2.22.32 2.23.13 2.23.15 2.23.16 2.23.18 2.24.12 2.24.14 2.24.18 2.25.14 2.26.6 2.28.10 2.28.13 2.28.14 2.28.7 2.29.1 2.29.2 2.29.3 2.3.0 2.3.1 2.3.2 2.3.3 2.30.18
tenweb-speed-optimizer / vendor / matthiasmullie / minify / src / JS.php
tenweb-speed-optimizer / vendor / matthiasmullie / minify / src Last commit date
Exceptions 3 years ago CSS.php 2 years ago Exception.php 3 years ago JS.php 2 years ago Minify.php 2 years ago
JS.php
619 lines
1 <?php
2
3 /**
4 * JavaScript minifier.
5 *
6 * Please report bugs on https://github.com/matthiasmullie/minify/issues
7 *
8 * @author Matthias Mullie <minify@mullie.eu>
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 <minify@mullie.eu>
21 * @author Tijs Verkoyen <minify@verkoyen.eu>
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 public function __construct()
126 {
127 call_user_func_array(array('\\MatthiasMullie\Minify\\Minify', '__construct'), func_get_args());
128
129 $dataDir = __DIR__ . '/../data/js/';
130 $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
131 $this->keywordsReserved = file($dataDir . 'keywords_reserved.txt', $options);
132 $this->keywordsBefore = file($dataDir . 'keywords_before.txt', $options);
133 $this->keywordsAfter = file($dataDir . 'keywords_after.txt', $options);
134 $this->operators = file($dataDir . 'operators.txt', $options);
135 $this->operatorsBefore = file($dataDir . 'operators_before.txt', $options);
136 $this->operatorsAfter = file($dataDir . 'operators_after.txt', $options);
137 }
138
139 /**
140 * Minify the data.
141 * Perform JS optimizations.
142 *
143 * @param string[optional] $path Path to write the data to
144 *
145 * @return string The minified data
146 */
147 public function execute($path = null)
148 {
149 $content = '';
150
151 /*
152 * Let's first take out strings, comments and regular expressions.
153 * All of these can contain JS code-like characters, and we should make
154 * sure any further magic ignores anything inside of these.
155 *
156 * Consider this example, where we should not strip any whitespace:
157 * var str = "a test";
158 *
159 * Comments will be removed altogether, strings and regular expressions
160 * will be replaced by placeholder text, which we'll restore later.
161 */
162 $this->extractStrings('\'"`');
163 $this->stripComments();
164 $this->extractRegex();
165
166 // loop files
167 foreach ($this->data as $source => $js) {
168 // take out strings, comments & regex (for which we've registered
169 // the regexes just a few lines earlier)
170 $js = $this->replace($js);
171
172 $js = $this->propertyNotation($js);
173 $js = $this->shortenBools($js);
174 $js = $this->stripWhitespace($js);
175
176 // combine js: separating the scripts by a ;
177 $content .= $js . ';';
178 }
179
180 // clean up leftover `;`s from the combination of multiple scripts
181 $content = ltrim($content, ';');
182 $content = (string) substr($content, 0, -1);
183
184 /*
185 * Earlier, we extracted strings & regular expressions and replaced them
186 * with placeholder text. This will restore them.
187 */
188 $content = $this->restoreExtractedData($content);
189
190 return $content;
191 }
192
193 /**
194 * Strip comments from source code.
195 */
196 protected function stripComments()
197 {
198 $this->stripMultilineComments();
199
200 // single-line comments
201 $this->registerPattern('/\/\/.*$/m', '');
202 }
203
204 /**
205 * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
206 *
207 * The content inside the regex can contain characters that may be confused
208 * for JS code: e.g. it could contain whitespace it needs to match & we
209 * don't want to strip whitespace in there.
210 *
211 * The regex can be pretty simple: we don't have to care about comments,
212 * (which also use slashes) because stripComments() will have stripped those
213 * already.
214 *
215 * This method will replace all string content with simple REGEX#
216 * placeholder text, so we've rid all regular expressions from characters
217 * that may be misinterpreted. Original regex content will be saved in
218 * $this->extracted and after doing all other minifying, we can restore the
219 * original content via restoreRegex()
220 */
221 protected function extractRegex()
222 {
223 // PHP only supports $this inside anonymous functions since 5.4
224 $minifier = $this;
225 $callback = function ($match) use ($minifier) {
226 $count = count($minifier->extracted);
227 $placeholder = '"' . $count . '"';
228 $minifier->extracted[$placeholder] = $match[0];
229
230 return $placeholder;
231 };
232
233 // match all chars except `/` and `\`
234 // `\` is allowed though, along with whatever char follows (which is the
235 // one being escaped)
236 // this should allow all chars, except for an unescaped `/` (= the one
237 // closing the regex)
238 // then also ignore bare `/` inside `[]`, where they don't need to be
239 // escaped: anything inside `[]` can be ignored safely
240 $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
241
242 // a regular expression can only be followed by a few operators or some
243 // of the RegExp methods (a `\` followed by a variable or value is
244 // likely part of a division, not a regex)
245 $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
246 $before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|' . implode('|', $keywords) . ')\s*';
247 $propertiesAndMethods = array(
248 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
249 'constructor',
250 'flags',
251 'global',
252 'ignoreCase',
253 'multiline',
254 'source',
255 'sticky',
256 'unicode',
257 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
258 'compile(',
259 'exec(',
260 'test(',
261 'toSource(',
262 'toString(',
263 );
264 $delimiters = array_fill(0, count($propertiesAndMethods), '/');
265 $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
266 $after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.(' . implode('|', $propertiesAndMethods) . ')))';
267 $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
268
269 // regular expressions following a `)` are rather annoying to detect...
270 // quite often, `/` after `)` is a division operator & if it happens to
271 // be followed by another one (or a comment), it is likely to be
272 // confused for a regular expression
273 // however, it's perfectly possible for a regex to follow a `)`: after
274 // a single-line `if()`, `while()`, ... statement, for example
275 // since, when they occur like that, they're always the start of a
276 // statement, there's only a limited amount of ways they can be useful:
277 // by calling the regex methods directly
278 // if a regex following `)` is not followed by `.<property or method>`,
279 // it's quite likely not a regex
280 $before = '\)\s*';
281 $after = '(?=\s*\.(' . implode('|', $propertiesAndMethods) . '))';
282 $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
283
284 // 1 more edge case: a regex can be followed by a lot more operators or
285 // keywords if there's a newline (ASI) in between, where the operator
286 // actually starts a new statement
287 // (https://github.com/matthiasmullie/minify/issues/56)
288 $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
289 $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
290 $after = '(?=\s*\n\s*(' . implode('|', $operators) . '))';
291 $this->registerPattern('/' . $pattern . $after . '/', $callback);
292 }
293
294 /**
295 * Strip whitespace.
296 *
297 * We won't strip *all* whitespace, but as much as possible. The thing that
298 * we'll preserve are newlines we're unsure about.
299 * JavaScript doesn't require statements to be terminated with a semicolon.
300 * It will automatically fix missing semicolons with ASI (automatic semi-
301 * colon insertion) at the end of line causing errors (without semicolon.)
302 *
303 * Because it's sometimes hard to tell if a newline is part of a statement
304 * that should be terminated or not, we'll just leave some of them alone.
305 *
306 * @param string $content The content to strip the whitespace for
307 *
308 * @return string
309 */
310 protected function stripWhitespace($content)
311 {
312 // uniform line endings, make them all line feed
313 $content = str_replace(array("\r\n", "\r"), "\n", $content);
314
315 // collapse all non-line feed whitespace into a single space
316 $content = preg_replace('/[^\S\n]+/', ' ', $content);
317
318 // strip leading & trailing whitespace
319 $content = str_replace(array(" \n", "\n "), "\n", $content);
320
321 // collapse consecutive line feeds into just 1
322 $content = preg_replace('/\n+/', "\n", $content);
323
324 $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
325 $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
326 $operators = $this->getOperatorsForRegex($this->operators, '/');
327 $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
328 $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
329
330 // strip whitespace that ends in (or next line begin with) an operator
331 // that allows statements to be broken up over multiple lines
332 unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
333 $content = preg_replace(
334 array(
335 '/(' . implode('|', $operatorsBefore) . ')\s+/',
336 '/\s+(' . implode('|', $operatorsAfter) . ')/',
337 ),
338 '\\1',
339 $content
340 );
341
342 // make sure + and - can't be mistaken for, or joined into ++ and --
343 $content = preg_replace(
344 array(
345 '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
346 '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
347 ),
348 '\\1',
349 $content
350 );
351
352 // collapse whitespace around reserved words into single space
353 $content = preg_replace('/(^|[;\}\s])\K(' . implode('|', $keywordsBefore) . ')\s+/', '\\2 ', $content);
354 $content = preg_replace('/\s+(' . implode('|', $keywordsAfter) . ')(?=([;\{\s]|$))/', ' \\1', $content);
355
356 /*
357 * We didn't strip whitespace after a couple of operators because they
358 * could be used in different contexts and we can't be sure it's ok to
359 * strip the newlines. However, we can safely strip any non-line feed
360 * whitespace that follows them.
361 */
362 $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
363 $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
364 $content = preg_replace('/(' . implode('|', $operatorsDiffBefore) . ')[^\S\n]+/', '\\1', $content);
365 $content = preg_replace('/[^\S\n]+(' . implode('|', $operatorsDiffAfter) . ')/', '\\1', $content);
366
367 /*
368 * Whitespace after `return` can be omitted in a few occasions
369 * (such as when followed by a string or regex)
370 * Same for whitespace in between `)` and `{`, or between `{` and some
371 * keywords.
372 */
373 $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
374 $content = preg_replace('/\)\s+\{/', '){', $content);
375 $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
376
377 /*
378 * Get rid of double semicolons, except where they can be used like:
379 * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
380 * I'll safeguard these double semicolons inside for-loops by
381 * temporarily replacing them with an invalid condition: they won't have
382 * a double semicolon and will be easy to spot to restore afterwards.
383 */
384 $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
385 $content = preg_replace('/;+/', ';', $content);
386 $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
387
388 /*
389 * Next, we'll be removing all semicolons where ASI kicks in.
390 * for-loops however, can have an empty body (ending in only a
391 * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
392 * Here, nothing happens during the loop; it's just used to keep
393 * increasing `i`. With that ; omitted, the next line would be expected
394 * to be the for-loop's body... Same goes for while loops.
395 * I'm going to double that semicolon (if any) so after the next line,
396 * which strips semicolons here & there, we're still left with this one.
397 * Note the special recursive construct in the three inner parts of the for:
398 * (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
399 * functions bodies, e.g.: i<arr.map(function(e){return e}).length.
400 * Also note that the construct is applied only once and multiplied
401 * for each part of the for, otherwise it risks a catastrophic backtracking.
402 * The limitation is that it will not allow closures in more than one
403 * of the three parts for a specific for() case.
404 * REGEX throwing catastrophic backtracking: $content = preg_replace('/(for\([^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*\));(\}|$)/s', '\\1;;\\8', $content);
405 */
406 $content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
407 $content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
408 $content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
409
410 $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
411
412 /*
413 * Do the same for the if's that don't have a body but are followed by ;}
414 */
415 $content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
416
417 /*
418 * Below will also keep `;` after a `do{}while();` along with `while();`
419 * While these could be stripped after do-while, detecting this
420 * distinction is cumbersome, so I'll play it safe and make sure `;`
421 * after any kind of `while` is kept.
422 */
423 $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
424
425 /*
426 * We also can't strip empty else-statements. Even though they're
427 * useless and probably shouldn't be in the code in the first place, we
428 * shouldn't be stripping the `;` that follows it as it breaks the code.
429 * We can just remove those useless else-statements completely.
430 *
431 * @see https://github.com/matthiasmullie/minify/issues/91
432 */
433 $content = preg_replace('/else;/s', '', $content);
434
435 /*
436 * We also don't really want to terminate statements followed by closing
437 * curly braces (which we've ignored completely up until now) or end-of-
438 * script: ASI will kick in here & we're all about minifying.
439 * Semicolons at beginning of the file don't make any sense either.
440 */
441 $content = preg_replace('/;(\}|$)/s', '\\1', $content);
442 $content = ltrim($content, ';');
443
444 // get rid of remaining whitespace af beginning/end
445 return trim($content);
446 }
447
448 /**
449 * We'll strip whitespace around certain operators with regular expressions.
450 * This will prepare the given array by escaping all characters.
451 *
452 * @param string[] $operators
453 * @param string $delimiter
454 *
455 * @return string[]
456 */
457 protected function getOperatorsForRegex(array $operators, $delimiter = '/')
458 {
459 // escape operators for use in regex
460 $delimiters = array_fill(0, count($operators), $delimiter);
461 $escaped = array_map('preg_quote', $operators, $delimiters);
462
463 $operators = array_combine($operators, $escaped);
464
465 // ignore + & - for now, they'll get special treatment
466 unset($operators['+'], $operators['-']);
467
468 // dot can not just immediately follow a number; it can be confused for
469 // decimal point, or calling a method on it, e.g. 42 .toString()
470 $operators['.'] = '(?<![0-9]\s)\.';
471
472 // don't confuse = with other assignment shortcuts (e.g. +=)
473 $chars = preg_quote('+-*\=<>%&|', $delimiter);
474 $operators['='] = '(?<![' . $chars . '])\=';
475
476 return $operators;
477 }
478
479 /**
480 * We'll strip whitespace around certain keywords with regular expressions.
481 * This will prepare the given array by escaping all characters.
482 *
483 * @param string[] $keywords
484 * @param string $delimiter
485 *
486 * @return string[]
487 */
488 protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
489 {
490 // escape keywords for use in regex
491 $delimiter = array_fill(0, count($keywords), $delimiter);
492 $escaped = array_map('preg_quote', $keywords, $delimiter);
493
494 // add word boundaries
495 array_walk($keywords, function ($value) {
496 return '\b' . $value . '\b';
497 });
498
499 $keywords = array_combine($keywords, $escaped);
500
501 return $keywords;
502 }
503
504 /**
505 * Replaces all occurrences of array['key'] by array.key.
506 *
507 * @param string $content
508 *
509 * @return string
510 */
511 protected function propertyNotation($content)
512 {
513 // PHP only supports $this inside anonymous functions since 5.4
514 $minifier = $this;
515 $keywords = $this->keywordsReserved;
516 $callback = function ($match) use ($minifier, $keywords) {
517 $property = trim($minifier->extracted[$match[1]], '\'"');
518
519 /*
520 * Check if the property is a reserved keyword. In this context (as
521 * property of an object literal/array) it shouldn't matter, but IE8
522 * freaks out with "Expected identifier".
523 */
524 if (in_array($property, $keywords)) {
525 return $match[0];
526 }
527
528 /*
529 * See if the property is in a variable-like format (e.g.
530 * array['key-here'] can't be replaced by array.key-here since '-'
531 * is not a valid character there.
532 */
533 if (!preg_match('/^' . $minifier::REGEX_VARIABLE . '$/u', $property)) {
534 return $match[0];
535 }
536
537 return '.' . $property;
538 };
539
540 /*
541 * Figure out if previous character is a variable name (of the array
542 * we want to use property notation on) - this is to make sure
543 * standalone ['value'] arrays aren't confused for keys-of-an-array.
544 * We can (and only have to) check the last character, because PHP's
545 * regex implementation doesn't allow unfixed-length look-behind
546 * assertions.
547 */
548 preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
549 $previousChar = $previousChar[1];
550
551 /*
552 * Make sure word preceding the ['value'] is not a keyword, e.g.
553 * return['x']. Because -again- PHP's regex implementation doesn't allow
554 * unfixed-length look-behind assertions, I'm just going to do a lot of
555 * separate look-behind assertions, one for each keyword.
556 */
557 $keywords = $this->getKeywordsForRegex($keywords);
558 $keywords = '(?<!' . implode(')(?<!', $keywords) . ')';
559
560 return preg_replace_callback('/(?<=' . $previousChar . '|\])' . $keywords . '\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
561 }
562
563 /**
564 * Replaces true & false by !0 and !1.
565 *
566 * @param string $content
567 *
568 * @return string
569 */
570 protected function shortenBools($content)
571 {
572 /*
573 * 'true' or 'false' could be used as property names (which may be
574 * followed by whitespace) - we must not replace those!
575 * Since PHP doesn't allow variable-length (to account for the
576 * whitespace) lookbehind assertions, I need to capture the leading
577 * character and check if it's a `.`
578 */
579 $callback = function ($match) {
580 if (trim($match[1]) === '.') {
581 return $match[0];
582 }
583
584 return $match[1] . ($match[2] === 'true' ? '!0' : '!1');
585 };
586 $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
587
588 // for(;;) is exactly the same as while(true), but shorter :)
589 $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
590
591 // now make sure we didn't turn any do ... while(true) into do ... for(;;)
592 preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
593
594 // go backward to make sure positional offsets aren't altered when $content changes
595 $dos = array_reverse($dos);
596 foreach ($dos as $do) {
597 $offsetDo = $do[0][1];
598
599 // find all `while` (now `for`) following `do`: one of those must be
600 // associated with the `do` and be turned back into `while`
601 preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
602 foreach ($whiles as $while) {
603 $offsetWhile = $while[0][1];
604
605 $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
606 $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
607 if ($open === $close) {
608 // only restore `while` if amount of `{` and `}` are the same;
609 // otherwise, that `for` isn't associated with this `do`
610 $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
611 break;
612 }
613 }
614 }
615
616 return $content;
617 }
618 }
619