Subversion Repositories oidplus

Rev

Rev 1308 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1308 Rev 1469
1
<?php
1
<?php
2
 
2
 
3
/**
3
/**
4
 * JavaScript minifier.
4
 * JavaScript minifier.
5
 *
5
 *
6
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
6
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
7
 *
7
 *
8
 * @author Matthias Mullie <minify@mullie.eu>
8
 * @author Matthias Mullie <minify@mullie.eu>
9
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
9
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
10
 * @license MIT License
10
 * @license MIT License
11
 */
11
 */
12
 
12
 
13
namespace MatthiasMullie\Minify;
13
namespace MatthiasMullie\Minify;
14
 
14
 
15
/**
15
/**
16
 * JavaScript Minifier Class.
16
 * JavaScript Minifier Class.
17
 *
17
 *
18
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
18
 * Please report bugs on https://github.com/matthiasmullie/minify/issues
19
 *
19
 *
20
 * @author Matthias Mullie <minify@mullie.eu>
20
 * @author Matthias Mullie <minify@mullie.eu>
21
 * @author Tijs Verkoyen <minify@verkoyen.eu>
21
 * @author Tijs Verkoyen <minify@verkoyen.eu>
22
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
22
 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
23
 * @license MIT License
23
 * @license MIT License
24
 */
24
 */
25
class JS extends Minify
25
class JS extends Minify
26
{
26
{
27
    /**
27
    /**
28
     * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
28
     * Var-matching regex based on http://stackoverflow.com/a/9337047/802993.
29
     *
29
     *
30
     * Note that regular expressions using that bit must have the PCRE_UTF8
30
     * Note that regular expressions using that bit must have the PCRE_UTF8
31
     * pattern modifier (/u) set.
31
     * pattern modifier (/u) set.
32
     *
32
     *
33
     * @internal
33
     * @internal
34
     *
34
     *
35
     * @var string
35
     * @var string
36
     */
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';
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
 
38
 
39
    /**
39
    /**
40
     * Full list of JavaScript reserved words.
40
     * Full list of JavaScript reserved words.
41
     * Will be loaded from /data/js/keywords_reserved.txt.
41
     * Will be loaded from /data/js/keywords_reserved.txt.
42
     *
42
     *
43
     * @see https://mathiasbynens.be/notes/reserved-keywords
43
     * @see https://mathiasbynens.be/notes/reserved-keywords
44
     *
44
     *
45
     * @var string[]
45
     * @var string[]
46
     */
46
     */
47
    protected $keywordsReserved = array();
47
    protected $keywordsReserved = array();
48
 
48
 
49
    /**
49
    /**
50
     * List of JavaScript reserved words that accept a <variable, value, ...>
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
51
     * after them. Some end of lines are not the end of a statement, like with
52
     * these keywords.
52
     * these keywords.
53
     *
53
     *
54
     * E.g.: we shouldn't insert a ; after this else
54
     * E.g.: we shouldn't insert a ; after this else
55
     * else
55
     * else
56
     *     console.log('this is quite fine')
56
     *     console.log('this is quite fine')
57
     *
57
     *
58
     * Will be loaded from /data/js/keywords_before.txt
58
     * Will be loaded from /data/js/keywords_before.txt
59
     *
59
     *
60
     * @var string[]
60
     * @var string[]
61
     */
61
     */
62
    protected $keywordsBefore = array();
62
    protected $keywordsBefore = array();
63
 
63
 
64
    /**
64
    /**
65
     * List of JavaScript reserved words that accept a <variable, value, ...>
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
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.
67
     * continued by one of these keywords on the newline.
68
     *
68
     *
69
     * E.g.: we shouldn't insert a ; before this instanceof
69
     * E.g.: we shouldn't insert a ; before this instanceof
70
     * variable
70
     * variable
71
     *     instanceof String
71
     *     instanceof String
72
     *
72
     *
73
     * Will be loaded from /data/js/keywords_after.txt
73
     * Will be loaded from /data/js/keywords_after.txt
74
     *
74
     *
75
     * @var string[]
75
     * @var string[]
76
     */
76
     */
77
    protected $keywordsAfter = array();
77
    protected $keywordsAfter = array();
78
 
78
 
79
    /**
79
    /**
80
     * List of all JavaScript operators.
80
     * List of all JavaScript operators.
81
     *
81
     *
82
     * Will be loaded from /data/js/operators.txt
82
     * Will be loaded from /data/js/operators.txt
83
     *
83
     *
84
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
84
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
85
     *
85
     *
86
     * @var string[]
86
     * @var string[]
87
     */
87
     */
88
    protected $operators = array();
88
    protected $operators = array();
89
 
89
 
90
    /**
90
    /**
91
     * List of JavaScript operators that accept a <variable, value, ...> after
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
92
     * them. Some end of lines are not the end of a statement, like with these
93
     * operators.
93
     * operators.
94
     *
94
     *
95
     * Note: Most operators are fine, we've only removed ++ and --.
95
     * Note: Most operators are fine, we've only removed ++ and --.
96
     * ++ & -- have to be joined with the value they're in-/decrementing.
96
     * ++ & -- have to be joined with the value they're in-/decrementing.
97
     *
97
     *
98
     * Will be loaded from /data/js/operators_before.txt
98
     * Will be loaded from /data/js/operators_before.txt
99
     *
99
     *
100
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
100
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
101
     *
101
     *
102
     * @var string[]
102
     * @var string[]
103
     */
103
     */
104
    protected $operatorsBefore = array();
104
    protected $operatorsBefore = array();
105
 
105
 
106
    /**
106
    /**
107
     * List of JavaScript operators that accept a <variable, value, ...> before
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
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.
109
     * continued by one of these operators on the newline.
110
     *
110
     *
111
     * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
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.
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.
113
     * ++ & -- have to be joined with the value they're in-/decrementing.
114
     * ) & ] are "special" in that they have lots or usecases. () for example
114
     * ) & ] are "special" in that they have lots or usecases. () for example
115
     * is used for function calls, for grouping, in if () and for (), ...
115
     * is used for function calls, for grouping, in if () and for (), ...
116
     *
116
     *
117
     * Will be loaded from /data/js/operators_after.txt
117
     * Will be loaded from /data/js/operators_after.txt
118
     *
118
     *
119
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
119
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
120
     *
120
     *
121
     * @var string[]
121
     * @var string[]
122
     */
122
     */
123
    protected $operatorsAfter = array();
123
    protected $operatorsAfter = array();
124
 
124
 
125
    /**
-
 
126
     * {@inheritdoc}
-
 
127
     */
-
 
128
    public function __construct()
125
    public function __construct()
129
    {
126
    {
130
        call_user_func_array(array('\\MatthiasMullie\Minify\\Minify', '__construct'), func_get_args());
127
        call_user_func_array(array('\\MatthiasMullie\Minify\\Minify', '__construct'), func_get_args());
131
 
128
 
132
        $dataDir = __DIR__ . '/../data/js/';
129
        $dataDir = __DIR__ . '/../data/js/';
133
        $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
130
        $options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
134
        $this->keywordsReserved = file($dataDir . 'keywords_reserved.txt', $options);
131
        $this->keywordsReserved = file($dataDir . 'keywords_reserved.txt', $options);
135
        $this->keywordsBefore = file($dataDir . 'keywords_before.txt', $options);
132
        $this->keywordsBefore = file($dataDir . 'keywords_before.txt', $options);
136
        $this->keywordsAfter = file($dataDir . 'keywords_after.txt', $options);
133
        $this->keywordsAfter = file($dataDir . 'keywords_after.txt', $options);
137
        $this->operators = file($dataDir . 'operators.txt', $options);
134
        $this->operators = file($dataDir . 'operators.txt', $options);
138
        $this->operatorsBefore = file($dataDir . 'operators_before.txt', $options);
135
        $this->operatorsBefore = file($dataDir . 'operators_before.txt', $options);
139
        $this->operatorsAfter = file($dataDir . 'operators_after.txt', $options);
136
        $this->operatorsAfter = file($dataDir . 'operators_after.txt', $options);
140
    }
137
    }
141
 
138
 
142
    /**
139
    /**
143
     * Minify the data.
140
     * Minify the data.
144
     * Perform JS optimizations.
141
     * Perform JS optimizations.
145
     *
142
     *
146
     * @param string[optional] $path Path to write the data to
143
     * @param string[optional] $path Path to write the data to
147
     *
144
     *
148
     * @return string The minified data
145
     * @return string The minified data
149
     */
146
     */
150
    public function execute($path = null)
147
    public function execute($path = null)
151
    {
148
    {
152
        $content = '';
149
        $content = '';
153
 
150
 
154
        /*
151
        /*
155
         * Let's first take out strings, comments and regular expressions.
152
         * Let's first take out strings, comments and regular expressions.
156
         * All of these can contain JS code-like characters, and we should make
153
         * All of these can contain JS code-like characters, and we should make
157
         * sure any further magic ignores anything inside of these.
154
         * sure any further magic ignores anything inside of these.
158
         *
155
         *
159
         * Consider this example, where we should not strip any whitespace:
156
         * Consider this example, where we should not strip any whitespace:
160
         * var str = "a   test";
157
         * var str = "a   test";
161
         *
158
         *
162
         * Comments will be removed altogether, strings and regular expressions
159
         * Comments will be removed altogether, strings and regular expressions
163
         * will be replaced by placeholder text, which we'll restore later.
160
         * will be replaced by placeholder text, which we'll restore later.
164
         */
161
         */
165
        $this->extractStrings('\'"`');
162
        $this->extractStrings('\'"`');
166
        $this->stripComments();
163
        $this->stripComments();
167
        $this->extractRegex();
164
        $this->extractRegex();
168
 
165
 
169
        // loop files
166
        // loop files
170
        foreach ($this->data as $source => $js) {
167
        foreach ($this->data as $source => $js) {
171
            // take out strings, comments & regex (for which we've registered
168
            // take out strings, comments & regex (for which we've registered
172
            // the regexes just a few lines earlier)
169
            // the regexes just a few lines earlier)
173
            $js = $this->replace($js);
170
            $js = $this->replace($js);
174
 
171
 
175
            $js = $this->propertyNotation($js);
172
            $js = $this->propertyNotation($js);
176
            $js = $this->shortenBools($js);
173
            $js = $this->shortenBools($js);
177
            $js = $this->stripWhitespace($js);
174
            $js = $this->stripWhitespace($js);
178
 
175
 
179
            // combine js: separating the scripts by a ;
176
            // combine js: separating the scripts by a ;
180
            $content .= $js . ';';
177
            $content .= $js . ';';
181
        }
178
        }
182
 
179
 
183
        // clean up leftover `;`s from the combination of multiple scripts
180
        // clean up leftover `;`s from the combination of multiple scripts
184
        $content = ltrim($content, ';');
181
        $content = ltrim($content, ';');
185
        $content = (string) substr($content, 0, -1);
182
        $content = (string) substr($content, 0, -1);
186
 
183
 
187
        /*
184
        /*
188
         * Earlier, we extracted strings & regular expressions and replaced them
185
         * Earlier, we extracted strings & regular expressions and replaced them
189
         * with placeholder text. This will restore them.
186
         * with placeholder text. This will restore them.
190
         */
187
         */
191
        $content = $this->restoreExtractedData($content);
188
        $content = $this->restoreExtractedData($content);
192
 
189
 
193
        return $content;
190
        return $content;
194
    }
191
    }
195
 
192
 
196
    /**
193
    /**
197
     * Strip comments from source code.
194
     * Strip comments from source code.
198
     */
195
     */
199
    protected function stripComments()
196
    protected function stripComments()
200
    {
197
    {
201
        $this->stripMultilineComments();
198
        $this->stripMultilineComments();
202
 
199
 
203
        // single-line comments
200
        // single-line comments
204
        $this->registerPattern('/\/\/.*$/m', '');
201
        $this->registerPattern('/\/\/.*$/m', '');
205
    }
202
    }
206
 
203
 
207
    /**
204
    /**
208
     * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
205
     * JS can have /-delimited regular expressions, like: /ab+c/.match(string).
209
     *
206
     *
210
     * The content inside the regex can contain characters that may be confused
207
     * 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
208
     * for JS code: e.g. it could contain whitespace it needs to match & we
212
     * don't want to strip whitespace in there.
209
     * don't want to strip whitespace in there.
213
     *
210
     *
214
     * The regex can be pretty simple: we don't have to care about comments,
211
     * 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
212
     * (which also use slashes) because stripComments() will have stripped those
216
     * already.
213
     * already.
217
     *
214
     *
218
     * This method will replace all string content with simple REGEX#
215
     * This method will replace all string content with simple REGEX#
219
     * placeholder text, so we've rid all regular expressions from characters
216
     * placeholder text, so we've rid all regular expressions from characters
220
     * that may be misinterpreted. Original regex content will be saved in
217
     * that may be misinterpreted. Original regex content will be saved in
221
     * $this->extracted and after doing all other minifying, we can restore the
218
     * $this->extracted and after doing all other minifying, we can restore the
222
     * original content via restoreRegex()
219
     * original content via restoreRegex()
223
     */
220
     */
224
    protected function extractRegex()
221
    protected function extractRegex()
225
    {
222
    {
226
        // PHP only supports $this inside anonymous functions since 5.4
223
        // PHP only supports $this inside anonymous functions since 5.4
227
        $minifier = $this;
224
        $minifier = $this;
228
        $callback = function ($match) use ($minifier) {
225
        $callback = function ($match) use ($minifier) {
229
            $count = count($minifier->extracted);
226
            $count = count($minifier->extracted);
230
            $placeholder = '"' . $count . '"';
227
            $placeholder = '"' . $count . '"';
231
            $minifier->extracted[$placeholder] = $match[0];
228
            $minifier->extracted[$placeholder] = $match[0];
232
 
229
 
233
            return $placeholder;
230
            return $placeholder;
234
        };
231
        };
235
 
232
 
236
        // match all chars except `/` and `\`
233
        // match all chars except `/` and `\`
237
        // `\` is allowed though, along with whatever char follows (which is the
234
        // `\` is allowed though, along with whatever char follows (which is the
238
        // one being escaped)
235
        // one being escaped)
239
        // this should allow all chars, except for an unescaped `/` (= the one
236
        // this should allow all chars, except for an unescaped `/` (= the one
240
        // closing the regex)
237
        // closing the regex)
241
        // then also ignore bare `/` inside `[]`, where they don't need to be
238
        // then also ignore bare `/` inside `[]`, where they don't need to be
242
        // escaped: anything inside `[]` can be ignored safely
239
        // escaped: anything inside `[]` can be ignored safely
243
        $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
240
        $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
244
 
241
 
245
        // a regular expression can only be followed by a few operators or some
242
        // 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
243
        // of the RegExp methods (a `\` followed by a variable or value is
247
        // likely part of a division, not a regex)
244
        // likely part of a division, not a regex)
248
        $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return',  'typeof');
245
        $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return',  'typeof');
249
        $before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|' . implode('|', $keywords) . ')\s*';
246
        $before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|' . implode('|', $keywords) . ')\s*';
250
        $propertiesAndMethods = array(
247
        $propertiesAndMethods = array(
251
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
248
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
252
            'constructor',
249
            'constructor',
253
            'flags',
250
            'flags',
254
            'global',
251
            'global',
255
            'ignoreCase',
252
            'ignoreCase',
256
            'multiline',
253
            'multiline',
257
            'source',
254
            'source',
258
            'sticky',
255
            'sticky',
259
            'unicode',
256
            'unicode',
260
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
257
            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
261
            'compile(',
258
            'compile(',
262
            'exec(',
259
            'exec(',
263
            'test(',
260
            'test(',
264
            'toSource(',
261
            'toSource(',
265
            'toString(',
262
            'toString(',
266
        );
263
        );
267
        $delimiters = array_fill(0, count($propertiesAndMethods), '/');
264
        $delimiters = array_fill(0, count($propertiesAndMethods), '/');
268
        $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
265
        $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
269
        $after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.(' . implode('|', $propertiesAndMethods) . ')))';
266
        $after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.(' . implode('|', $propertiesAndMethods) . ')))';
270
        $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
267
        $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
271
 
268
 
272
        // regular expressions following a `)` are rather annoying to detect...
269
        // regular expressions following a `)` are rather annoying to detect...
273
        // quite often, `/` after `)` is a division operator & if it happens to
270
        // 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
271
        // be followed by another one (or a comment), it is likely to be
275
        // confused for a regular expression
272
        // confused for a regular expression
276
        // however, it's perfectly possible for a regex to follow a `)`: after
273
        // however, it's perfectly possible for a regex to follow a `)`: after
277
        // a single-line `if()`, `while()`, ... statement, for example
274
        // a single-line `if()`, `while()`, ... statement, for example
278
        // since, when they occur like that, they're always the start of a
275
        // 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:
276
        // statement, there's only a limited amount of ways they can be useful:
280
        // by calling the regex methods directly
277
        // by calling the regex methods directly
281
        // if a regex following `)` is not followed by `.<property or method>`,
278
        // if a regex following `)` is not followed by `.<property or method>`,
282
        // it's quite likely not a regex
279
        // it's quite likely not a regex
283
        $before = '\)\s*';
280
        $before = '\)\s*';
284
        $after = '(?=\s*\.(' . implode('|', $propertiesAndMethods) . '))';
281
        $after = '(?=\s*\.(' . implode('|', $propertiesAndMethods) . '))';
285
        $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
282
        $this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
286
 
283
 
287
        // 1 more edge case: a regex can be followed by a lot more operators or
284
        // 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
285
        // keywords if there's a newline (ASI) in between, where the operator
289
        // actually starts a new statement
286
        // actually starts a new statement
290
        // (https://github.com/matthiasmullie/minify/issues/56)
287
        // (https://github.com/matthiasmullie/minify/issues/56)
291
        $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
288
        $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
292
        $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
289
        $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
293
        $after = '(?=\s*\n\s*(' . implode('|', $operators) . '))';
290
        $after = '(?=\s*\n\s*(' . implode('|', $operators) . '))';
294
        $this->registerPattern('/' . $pattern . $after . '/', $callback);
291
        $this->registerPattern('/' . $pattern . $after . '/', $callback);
295
    }
292
    }
296
 
293
 
297
    /**
294
    /**
298
     * Strip whitespace.
295
     * Strip whitespace.
299
     *
296
     *
300
     * We won't strip *all* whitespace, but as much as possible. The thing that
297
     * We won't strip *all* whitespace, but as much as possible. The thing that
301
     * we'll preserve are newlines we're unsure about.
298
     * we'll preserve are newlines we're unsure about.
302
     * JavaScript doesn't require statements to be terminated with a semicolon.
299
     * JavaScript doesn't require statements to be terminated with a semicolon.
303
     * It will automatically fix missing semicolons with ASI (automatic semi-
300
     * It will automatically fix missing semicolons with ASI (automatic semi-
304
     * colon insertion) at the end of line causing errors (without semicolon.)
301
     * colon insertion) at the end of line causing errors (without semicolon.)
305
     *
302
     *
306
     * Because it's sometimes hard to tell if a newline is part of a statement
303
     * 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.
304
     * that should be terminated or not, we'll just leave some of them alone.
308
     *
305
     *
309
     * @param string $content The content to strip the whitespace for
306
     * @param string $content The content to strip the whitespace for
310
     *
307
     *
311
     * @return string
308
     * @return string
312
     */
309
     */
313
    protected function stripWhitespace($content)
310
    protected function stripWhitespace($content)
314
    {
311
    {
315
        // uniform line endings, make them all line feed
312
        // uniform line endings, make them all line feed
316
        $content = str_replace(array("\r\n", "\r"), "\n", $content);
313
        $content = str_replace(array("\r\n", "\r"), "\n", $content);
317
 
314
 
318
        // collapse all non-line feed whitespace into a single space
315
        // collapse all non-line feed whitespace into a single space
319
        $content = preg_replace('/[^\S\n]+/', ' ', $content);
316
        $content = preg_replace('/[^\S\n]+/', ' ', $content);
320
 
317
 
321
        // strip leading & trailing whitespace
318
        // strip leading & trailing whitespace
322
        $content = str_replace(array(" \n", "\n "), "\n", $content);
319
        $content = str_replace(array(" \n", "\n "), "\n", $content);
323
 
320
 
324
        // collapse consecutive line feeds into just 1
321
        // collapse consecutive line feeds into just 1
325
        $content = preg_replace('/\n+/', "\n", $content);
322
        $content = preg_replace('/\n+/', "\n", $content);
326
 
323
 
327
        $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
324
        $operatorsBefore = $this->getOperatorsForRegex($this->operatorsBefore, '/');
328
        $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
325
        $operatorsAfter = $this->getOperatorsForRegex($this->operatorsAfter, '/');
329
        $operators = $this->getOperatorsForRegex($this->operators, '/');
326
        $operators = $this->getOperatorsForRegex($this->operators, '/');
330
        $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
327
        $keywordsBefore = $this->getKeywordsForRegex($this->keywordsBefore, '/');
331
        $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
328
        $keywordsAfter = $this->getKeywordsForRegex($this->keywordsAfter, '/');
332
 
329
 
333
        // strip whitespace that ends in (or next line begin with) an operator
330
        // strip whitespace that ends in (or next line begin with) an operator
334
        // that allows statements to be broken up over multiple lines
331
        // that allows statements to be broken up over multiple lines
335
        unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
332
        unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
336
        $content = preg_replace(
333
        $content = preg_replace(
337
            array(
334
            array(
338
                '/(' . implode('|', $operatorsBefore) . ')\s+/',
335
                '/(' . implode('|', $operatorsBefore) . ')\s+/',
339
                '/\s+(' . implode('|', $operatorsAfter) . ')/',
336
                '/\s+(' . implode('|', $operatorsAfter) . ')/',
340
            ),
337
            ),
341
            '\\1',
338
            '\\1',
342
            $content
339
            $content
343
        );
340
        );
344
 
341
 
345
        // make sure + and - can't be mistaken for, or joined into ++ and --
342
        // make sure + and - can't be mistaken for, or joined into ++ and --
346
        $content = preg_replace(
343
        $content = preg_replace(
347
            array(
344
            array(
348
                '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
345
                '/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
349
                '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
346
                '/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
350
            ),
347
            ),
351
            '\\1',
348
            '\\1',
352
            $content
349
            $content
353
        );
350
        );
354
 
351
 
355
        // collapse whitespace around reserved words into single space
352
        // collapse whitespace around reserved words into single space
356
        $content = preg_replace('/(^|[;\}\s])\K(' . implode('|', $keywordsBefore) . ')\s+/', '\\2 ', $content);
353
        $content = preg_replace('/(^|[;\}\s])\K(' . implode('|', $keywordsBefore) . ')\s+/', '\\2 ', $content);
357
        $content = preg_replace('/\s+(' . implode('|', $keywordsAfter) . ')(?=([;\{\s]|$))/', ' \\1', $content);
354
        $content = preg_replace('/\s+(' . implode('|', $keywordsAfter) . ')(?=([;\{\s]|$))/', ' \\1', $content);
358
 
355
 
359
        /*
356
        /*
360
         * We didn't strip whitespace after a couple of operators because they
357
         * 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
358
         * 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
359
         * strip the newlines. However, we can safely strip any non-line feed
363
         * whitespace that follows them.
360
         * whitespace that follows them.
364
         */
361
         */
365
        $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
362
        $operatorsDiffBefore = array_diff($operators, $operatorsBefore);
366
        $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
363
        $operatorsDiffAfter = array_diff($operators, $operatorsAfter);
367
        $content = preg_replace('/(' . implode('|', $operatorsDiffBefore) . ')[^\S\n]+/', '\\1', $content);
364
        $content = preg_replace('/(' . implode('|', $operatorsDiffBefore) . ')[^\S\n]+/', '\\1', $content);
368
        $content = preg_replace('/[^\S\n]+(' . implode('|', $operatorsDiffAfter) . ')/', '\\1', $content);
365
        $content = preg_replace('/[^\S\n]+(' . implode('|', $operatorsDiffAfter) . ')/', '\\1', $content);
369
 
366
 
370
        /*
367
        /*
371
         * Whitespace after `return` can be omitted in a few occasions
368
         * Whitespace after `return` can be omitted in a few occasions
372
         * (such as when followed by a string or regex)
369
         * (such as when followed by a string or regex)
373
         * Same for whitespace in between `)` and `{`, or between `{` and some
370
         * Same for whitespace in between `)` and `{`, or between `{` and some
374
         * keywords.
371
         * keywords.
375
         */
372
         */
376
        $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
373
        $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
377
        $content = preg_replace('/\)\s+\{/', '){', $content);
374
        $content = preg_replace('/\)\s+\{/', '){', $content);
378
        $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
375
        $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
379
 
376
 
380
        /*
377
        /*
381
         * Get rid of double semicolons, except where they can be used like:
378
         * 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))".
379
         * "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
383
         * I'll safeguard these double semicolons inside for-loops by
380
         * I'll safeguard these double semicolons inside for-loops by
384
         * temporarily replacing them with an invalid condition: they won't have
381
         * temporarily replacing them with an invalid condition: they won't have
385
         * a double semicolon and will be easy to spot to restore afterwards.
382
         * a double semicolon and will be easy to spot to restore afterwards.
386
         */
383
         */
387
        $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
384
        $content = preg_replace('/\bfor\(([^;]*);;([^;]*)\)/', 'for(\\1;-;\\2)', $content);
388
        $content = preg_replace('/;+/', ';', $content);
385
        $content = preg_replace('/;+/', ';', $content);
389
        $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
386
        $content = preg_replace('/\bfor\(([^;]*);-;([^;]*)\)/', 'for(\\1;;\\2)', $content);
390
 
387
 
391
        /*
388
        /*
392
         * Next, we'll be removing all semicolons where ASI kicks in.
389
         * Next, we'll be removing all semicolons where ASI kicks in.
393
         * for-loops however, can have an empty body (ending in only a
390
         * 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);`
391
         * 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
392
         * Here, nothing happens during the loop; it's just used to keep
396
         * increasing `i`. With that ; omitted, the next line would be expected
393
         * increasing `i`. With that ; omitted, the next line would be expected
397
         * to be the for-loop's body... Same goes for while loops.
394
         * 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,
395
         * 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.
396
         * 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:
397
         * Note the special recursive construct in the three inner parts of the for:
401
         * (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
398
         * (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
402
         * functions bodies, e.g.: i<arr.map(function(e){return e}).length.
399
         * functions bodies, e.g.: i<arr.map(function(e){return e}).length.
403
         * Also note that the construct is applied only once and multiplied
400
         * Also note that the construct is applied only once and multiplied
404
         * for each part of the for, otherwise it risks a catastrophic backtracking.
401
         * 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
402
         * The limitation is that it will not allow closures in more than one
406
         * of the three parts for a specific for() case.
403
         * 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);
404
         * REGEX throwing catastrophic backtracking: $content = preg_replace('/(for\([^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*\));(\}|$)/s', '\\1;;\\8', $content);
408
         */
405
         */
409
        $content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
406
        $content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
410
        $content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
407
        $content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
411
        $content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
408
        $content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
412
 
409
 
413
        $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
410
        $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
414
 
411
 
415
        /*
412
        /*
416
         * Do the same for the if's that don't have a body but are followed by ;}
413
         * Do the same for the if's that don't have a body but are followed by ;}
417
         */
414
         */
418
        $content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
415
        $content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
419
 
416
 
420
        /*
417
        /*
421
         * Below will also keep `;` after a `do{}while();` along with `while();`
418
         * Below will also keep `;` after a `do{}while();` along with `while();`
422
         * While these could be stripped after do-while, detecting this
419
         * While these could be stripped after do-while, detecting this
423
         * distinction is cumbersome, so I'll play it safe and make sure `;`
420
         * distinction is cumbersome, so I'll play it safe and make sure `;`
424
         * after any kind of `while` is kept.
421
         * after any kind of `while` is kept.
425
         */
422
         */
426
        $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
423
        $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
427
 
424
 
428
        /*
425
        /*
429
         * We also can't strip empty else-statements. Even though they're
426
         * 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
427
         * 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.
428
         * shouldn't be stripping the `;` that follows it as it breaks the code.
432
         * We can just remove those useless else-statements completely.
429
         * We can just remove those useless else-statements completely.
433
         *
430
         *
434
         * @see https://github.com/matthiasmullie/minify/issues/91
431
         * @see https://github.com/matthiasmullie/minify/issues/91
435
         */
432
         */
436
        $content = preg_replace('/else;/s', '', $content);
433
        $content = preg_replace('/else;/s', '', $content);
437
 
434
 
438
        /*
435
        /*
439
         * We also don't really want to terminate statements followed by closing
436
         * 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-
437
         * 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.
438
         * script: ASI will kick in here & we're all about minifying.
442
         * Semicolons at beginning of the file don't make any sense either.
439
         * Semicolons at beginning of the file don't make any sense either.
443
         */
440
         */
444
        $content = preg_replace('/;(\}|$)/s', '\\1', $content);
441
        $content = preg_replace('/;(\}|$)/s', '\\1', $content);
445
        $content = ltrim($content, ';');
442
        $content = ltrim($content, ';');
446
 
443
 
447
        // get rid of remaining whitespace af beginning/end
444
        // get rid of remaining whitespace af beginning/end
448
        return trim($content);
445
        return trim($content);
449
    }
446
    }
450
 
447
 
451
    /**
448
    /**
452
     * We'll strip whitespace around certain operators with regular expressions.
449
     * We'll strip whitespace around certain operators with regular expressions.
453
     * This will prepare the given array by escaping all characters.
450
     * This will prepare the given array by escaping all characters.
454
     *
451
     *
455
     * @param string[] $operators
452
     * @param string[] $operators
456
     * @param string   $delimiter
453
     * @param string   $delimiter
457
     *
454
     *
458
     * @return string[]
455
     * @return string[]
459
     */
456
     */
460
    protected function getOperatorsForRegex(array $operators, $delimiter = '/')
457
    protected function getOperatorsForRegex(array $operators, $delimiter = '/')
461
    {
458
    {
462
        // escape operators for use in regex
459
        // escape operators for use in regex
463
        $delimiters = array_fill(0, count($operators), $delimiter);
460
        $delimiters = array_fill(0, count($operators), $delimiter);
464
        $escaped = array_map('preg_quote', $operators, $delimiters);
461
        $escaped = array_map('preg_quote', $operators, $delimiters);
465
 
462
 
466
        $operators = array_combine($operators, $escaped);
463
        $operators = array_combine($operators, $escaped);
467
 
464
 
468
        // ignore + & - for now, they'll get special treatment
465
        // ignore + & - for now, they'll get special treatment
469
        unset($operators['+'], $operators['-']);
466
        unset($operators['+'], $operators['-']);
470
 
467
 
471
        // dot can not just immediately follow a number; it can be confused for
468
        // 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()
469
        // decimal point, or calling a method on it, e.g. 42 .toString()
473
        $operators['.'] = '(?<![0-9]\s)\.';
470
        $operators['.'] = '(?<![0-9]\s)\.';
474
 
471
 
475
        // don't confuse = with other assignment shortcuts (e.g. +=)
472
        // don't confuse = with other assignment shortcuts (e.g. +=)
476
        $chars = preg_quote('+-*\=<>%&|', $delimiter);
473
        $chars = preg_quote('+-*\=<>%&|', $delimiter);
477
        $operators['='] = '(?<![' . $chars . '])\=';
474
        $operators['='] = '(?<![' . $chars . '])\=';
478
 
475
 
479
        return $operators;
476
        return $operators;
480
    }
477
    }
481
 
478
 
482
    /**
479
    /**
483
     * We'll strip whitespace around certain keywords with regular expressions.
480
     * We'll strip whitespace around certain keywords with regular expressions.
484
     * This will prepare the given array by escaping all characters.
481
     * This will prepare the given array by escaping all characters.
485
     *
482
     *
486
     * @param string[] $keywords
483
     * @param string[] $keywords
487
     * @param string   $delimiter
484
     * @param string   $delimiter
488
     *
485
     *
489
     * @return string[]
486
     * @return string[]
490
     */
487
     */
491
    protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
488
    protected function getKeywordsForRegex(array $keywords, $delimiter = '/')
492
    {
489
    {
493
        // escape keywords for use in regex
490
        // escape keywords for use in regex
494
        $delimiter = array_fill(0, count($keywords), $delimiter);
491
        $delimiter = array_fill(0, count($keywords), $delimiter);
495
        $escaped = array_map('preg_quote', $keywords, $delimiter);
492
        $escaped = array_map('preg_quote', $keywords, $delimiter);
496
 
493
 
497
        // add word boundaries
494
        // add word boundaries
498
        array_walk($keywords, function ($value) {
495
        array_walk($keywords, function ($value) {
499
            return '\b' . $value . '\b';
496
            return '\b' . $value . '\b';
500
        });
497
        });
501
 
498
 
502
        $keywords = array_combine($keywords, $escaped);
499
        $keywords = array_combine($keywords, $escaped);
503
 
500
 
504
        return $keywords;
501
        return $keywords;
505
    }
502
    }
506
 
503
 
507
    /**
504
    /**
508
     * Replaces all occurrences of array['key'] by array.key.
505
     * Replaces all occurrences of array['key'] by array.key.
509
     *
506
     *
510
     * @param string $content
507
     * @param string $content
511
     *
508
     *
512
     * @return string
509
     * @return string
513
     */
510
     */
514
    protected function propertyNotation($content)
511
    protected function propertyNotation($content)
515
    {
512
    {
516
        // PHP only supports $this inside anonymous functions since 5.4
513
        // PHP only supports $this inside anonymous functions since 5.4
517
        $minifier = $this;
514
        $minifier = $this;
518
        $keywords = $this->keywordsReserved;
515
        $keywords = $this->keywordsReserved;
519
        $callback = function ($match) use ($minifier, $keywords) {
516
        $callback = function ($match) use ($minifier, $keywords) {
520
            $property = trim($minifier->extracted[$match[1]], '\'"');
517
            $property = trim($minifier->extracted[$match[1]], '\'"');
521
 
518
 
522
            /*
519
            /*
523
             * Check if the property is a reserved keyword. In this context (as
520
             * 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
521
             * property of an object literal/array) it shouldn't matter, but IE8
525
             * freaks out with "Expected identifier".
522
             * freaks out with "Expected identifier".
526
             */
523
             */
527
            if (in_array($property, $keywords)) {
524
            if (in_array($property, $keywords)) {
528
                return $match[0];
525
                return $match[0];
529
            }
526
            }
530
 
527
 
531
            /*
528
            /*
532
             * See if the property is in a variable-like format (e.g.
529
             * 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 '-'
530
             * array['key-here'] can't be replaced by array.key-here since '-'
534
             * is not a valid character there.
531
             * is not a valid character there.
535
             */
532
             */
536
            if (!preg_match('/^' . $minifier::REGEX_VARIABLE . '$/u', $property)) {
533
            if (!preg_match('/^' . $minifier::REGEX_VARIABLE . '$/u', $property)) {
537
                return $match[0];
534
                return $match[0];
538
            }
535
            }
539
 
536
 
540
            return '.' . $property;
537
            return '.' . $property;
541
        };
538
        };
542
 
539
 
543
        /*
540
        /*
544
         * Figure out if previous character is a variable name (of the array
541
         * 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
542
         * we want to use property notation on) - this is to make sure
546
         * standalone ['value'] arrays aren't confused for keys-of-an-array.
543
         * 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
544
         * We can (and only have to) check the last character, because PHP's
548
         * regex implementation doesn't allow unfixed-length look-behind
545
         * regex implementation doesn't allow unfixed-length look-behind
549
         * assertions.
546
         * assertions.
550
         */
547
         */
551
        preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
548
        preg_match('/(\[[^\]]+\])[^\]]*$/', static::REGEX_VARIABLE, $previousChar);
552
        $previousChar = $previousChar[1];
549
        $previousChar = $previousChar[1];
553
 
550
 
554
        /*
551
        /*
555
         * Make sure word preceding the ['value'] is not a keyword, e.g.
552
         * Make sure word preceding the ['value'] is not a keyword, e.g.
556
         * return['x']. Because -again- PHP's regex implementation doesn't allow
553
         * 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
554
         * unfixed-length look-behind assertions, I'm just going to do a lot of
558
         * separate look-behind assertions, one for each keyword.
555
         * separate look-behind assertions, one for each keyword.
559
         */
556
         */
560
        $keywords = $this->getKeywordsForRegex($keywords);
557
        $keywords = $this->getKeywordsForRegex($keywords);
561
        $keywords = '(?<!' . implode(')(?<!', $keywords) . ')';
558
        $keywords = '(?<!' . implode(')(?<!', $keywords) . ')';
562
 
559
 
563
        return preg_replace_callback('/(?<=' . $previousChar . '|\])' . $keywords . '\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
560
        return preg_replace_callback('/(?<=' . $previousChar . '|\])' . $keywords . '\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
564
    }
561
    }
565
 
562
 
566
    /**
563
    /**
567
     * Replaces true & false by !0 and !1.
564
     * Replaces true & false by !0 and !1.
568
     *
565
     *
569
     * @param string $content
566
     * @param string $content
570
     *
567
     *
571
     * @return string
568
     * @return string
572
     */
569
     */
573
    protected function shortenBools($content)
570
    protected function shortenBools($content)
574
    {
571
    {
575
        /*
572
        /*
576
         * 'true' or 'false' could be used as property names (which may be
573
         * 'true' or 'false' could be used as property names (which may be
577
         * followed by whitespace) - we must not replace those!
574
         * followed by whitespace) - we must not replace those!
578
         * Since PHP doesn't allow variable-length (to account for the
575
         * Since PHP doesn't allow variable-length (to account for the
579
         * whitespace) lookbehind assertions, I need to capture the leading
576
         * whitespace) lookbehind assertions, I need to capture the leading
580
         * character and check if it's a `.`
577
         * character and check if it's a `.`
581
         */
578
         */
582
        $callback = function ($match) {
579
        $callback = function ($match) {
583
            if (trim($match[1]) === '.') {
580
            if (trim($match[1]) === '.') {
584
                return $match[0];
581
                return $match[0];
585
            }
582
            }
586
 
583
 
587
            return $match[1] . ($match[2] === 'true' ? '!0' : '!1');
584
            return $match[1] . ($match[2] === 'true' ? '!0' : '!1');
588
        };
585
        };
589
        $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
586
        $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
590
 
587
 
591
        // for(;;) is exactly the same as while(true), but shorter :)
588
        // for(;;) is exactly the same as while(true), but shorter :)
592
        $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
589
        $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
593
 
590
 
594
        // now make sure we didn't turn any do ... while(true) into do ... for(;;)
591
        // 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);
592
        preg_match_all('/\bdo\b/', $content, $dos, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
596
 
593
 
597
        // go backward to make sure positional offsets aren't altered when $content changes
594
        // go backward to make sure positional offsets aren't altered when $content changes
598
        $dos = array_reverse($dos);
595
        $dos = array_reverse($dos);
599
        foreach ($dos as $do) {
596
        foreach ($dos as $do) {
600
            $offsetDo = $do[0][1];
597
            $offsetDo = $do[0][1];
601
 
598
 
602
            // find all `while` (now `for`) following `do`: one of those must be
599
            // find all `while` (now `for`) following `do`: one of those must be
603
            // associated with the `do` and be turned back into `while`
600
            // associated with the `do` and be turned back into `while`
604
            preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
601
            preg_match_all('/\bfor\(;;\)/', $content, $whiles, PREG_OFFSET_CAPTURE | PREG_SET_ORDER, $offsetDo);
605
            foreach ($whiles as $while) {
602
            foreach ($whiles as $while) {
606
                $offsetWhile = $while[0][1];
603
                $offsetWhile = $while[0][1];
607
 
604
 
608
                $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
605
                $open = substr_count($content, '{', $offsetDo, $offsetWhile - $offsetDo);
609
                $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
606
                $close = substr_count($content, '}', $offsetDo, $offsetWhile - $offsetDo);
610
                if ($open === $close) {
607
                if ($open === $close) {
611
                    // only restore `while` if amount of `{` and `}` are the same;
608
                    // only restore `while` if amount of `{` and `}` are the same;
612
                    // otherwise, that `for` isn't associated with this `do`
609
                    // otherwise, that `for` isn't associated with this `do`
613
                    $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
610
                    $content = substr_replace($content, 'while(!0)', $offsetWhile, strlen('for(;;)'));
614
                    break;
611
                    break;
615
                }
612
                }
616
            }
613
            }
617
        }
614
        }
618
 
615
 
619
        return $content;
616
        return $content;
620
    }
617
    }
621
}
618
}
622
 
619