Files
acai-vscode-webbase/lib/minifier.php

307 lines
12 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Based on <https://github.com/mecha-cms/extend.minify>
define('MINIFY_STRING', '"(?:[^"\\\]|\\\.)*"|\'(?:[^\'\\\]|\\\.)*\'');
define('MINIFY_COMMENT_CSS', '/\*[\s\S]*?\*/');
define('MINIFY_COMMENT_HTML', '<!\-{2}[\s\S]*?\-{2}>');
define('MINIFY_COMMENT_JS', '//[^\n]*');
define('MINIFY_PATTERN_JS', '/[^\n]+?/[gimuy]*');
define('MINIFY_HTML', '<[!/]?[a-zA-Z\d:.-]+[\s\S]*?>');
define('MINIFY_HTML_ENT', '&(?:[a-zA-Z\d]+|\#\d+|\#x[a-fA-F\d]+);');
define('MINIFY_HTML_KEEP', '<pre(?:\s[^<>]*?)?>[\s\S]*?</pre>|<code(?:\s[^<>]*?)?>[\s\S]*?</code>|<script(?:\s[^<>]*?)?>[\s\S]*?</script>|<style(?:\s[^<>]*?)?>[\s\S]*?</style>|<textarea(?:\s[^<>]*?)?>[\s\S]*?</textarea>');
// get URL
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] === 443 ? 'https' : 'http') . '://';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "");
$url = $protocol . $host;
// escape character
define('X', "\x1A");
// normalize linebreak(s)
function n($s) {
return str_replace(["\r\n", "\r"], "\n", $s);
}
// trim once
function tr($a, $b) {
if ($a && strpos($a, $b) === 0 && substr($a, -strlen($b)) === $b) {
return substr(substr($a, strlen($b)), 0, -strlen($b));
}
return $a;
}
function fn_minify($pattern, $input) {
return preg_split('#(' . implode('|', $pattern) . ')#', $input, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
}
function fn_minify_css($input, $comment = 2, $quote = 2) {
if (!is_string($input) || !$input = n(trim($input))) return $input;
$output = $prev = "";
foreach (fn_minify([MINIFY_COMMENT_CSS, MINIFY_STRING], $input) as $part) {
if (trim($part) === "") continue;
if ($comment !== 1 && strpos($part, '/*') === 0 && substr($part, -2) === '*/') {
if (
$comment === 2 && (
// Detect special comment(s) from the third character. It should be a `!` or `*` → `/*! keep */` or `/** keep */`
strpos('*!', $part[2]) !== false ||
// Detect license comment(s) from the content. It should contains character(s) like `@license`
stripos($part, '@licence') !== false || // noun
stripos($part, '@license') !== false || // verb
stripos($part, '@preserve') !== false
)
) {
$output .= $part;
}
continue;
}
if ($part[0] === '"' && substr($part, -1) === '"' || $part[0] === "'" && substr($part, -1) === "'") {
// Remove quote(s) where possible …
$q = $part[0];
if (
$quote !== 1 && (
// <https://www.w3.org/TR/CSS2/syndata.html#uri>
substr($prev, -4) === 'url(' && preg_match('#\burl\($#', $prev) ||
// <https://www.w3.org/TR/CSS2/syndata.html#characters>
substr($prev, -1) === '=' && preg_match('#^' . $q . '[a-zA-Z_][\w-]*?' . $q . '$#', $part)
)
) {
$part = tr($part, $q); // trim quote(s)
}
$output .= $part;
} else {
$output .= fn_minify_css_union($part);
}
$prev = $part;
}
return trim($output);
}
function fn_minify_css_union($input) {
if (stripos($input, 'calc(') !== false) {
// Keep important whitespace(s) in `calc()`
$input = preg_replace_callback('#\b(calc\()\s*(.*?)\s*\)#i', function($m) {
return $m[1] . preg_replace('#\s+#', X, $m[2]) . ')';
}, $input);
}
$input = preg_replace([
// Fix case for `#foo<space>[bar="baz"]`, `#foo<space>*` and `#foo<space>:first-child` [^1]
'#(?<=[\w])\s+(\*|\[|:[\w-]+)#',
// Fix case for `[bar="baz"]<space>.foo`, `*<space>.foo`, `:nth-child(2)<space>.foo` and `@media<space>(foo: bar)<space>and<space>(baz: qux)` [^2]
'#([*\]\)])\s+(?=[\w\#.])#', '#\b\s+\(#', '#\)\s+\b#',
// Minify HEX color code … [^3]
'#\#([a-f\d])\1([a-f\d])\2([a-f\d])\3\b#i',
// Remove whitespace(s) around punctuation(s) [^4]
'#\s*([~!@*\(\)+=\{\}\[\]:;,>\/])\s*#',
// Replace zero unit(s) with `0` [^5]
'#\b(?:0\.)?0([a-z]+\b)#i',
// Replace `0.6` with `.6` [^6]
'#\b0+\.(\d+)#',
// Replace `:0 0`, `:0 0 0` and `:0 0 0 0` with `:0` [^7]
'#:(0\s+){0,3}0(?=[!,;\)\}]|$)#',
// Replace `background(?:-position)?:(0|none)` with `background$1:0 0` [^8]
'#\b(background(?:-position)?):(?:0|none)([;,\}])#i',
// Replace `(border(?:-radius)?|outline):none` with `$1:0` [^9]
'#\b(border(?:-radius)?|outline):none\b#i',
// Remove empty selector(s) [^10]
'#(^|[\{\}])(?:[^\{\}]+)\{\}#',
// Remove the last semicolon and replace multiple semicolon(s) with a semicolon [^11]
'#;+([;\}])#',
// Replace multiple whitespace(s) with a space [^12]
'#\s+#'
], [
// [^1]
X . '$1',
// [^2]
'$1' . X, X . '(', ')' . X,
// [^3]
'#$1$2$3',
// [^4]
'$1',
// [^5]
'0',
// [^6]
'.$1',
// [^7]
':0',
// [^8]
'$1:0 0$2',
// [^9]
'$1:0',
// [^10]
'$1',
// [^11]
'$1',
// [^12]
' '
], $input);
return trim(str_replace(X, ' ', $input));
}
function fn_minify_html($input, $comment = 2, $quote = 1) {
if (!is_string($input) || !$input = n(trim($input))) return $input;
$output = $prev = "";
foreach (fn_minify([MINIFY_COMMENT_HTML, MINIFY_HTML_KEEP, MINIFY_HTML, MINIFY_HTML_ENT], $input) as $part) {
if ($part === "\n") continue;
if ($part !== ' ' && trim($part) === "" || $comment !== 1 && strpos($part, '<!--') === 0) {
// Detect IE conditional comment(s) by its closing tag …
if ($comment === 2 && substr($part, -12) === '<![endif]-->') {
$output .= $part;
}
continue;
}
if ($part[0] === '<' && substr($part, -1) === '>') {
$output .= fn_minify_html_union($part, $quote);
} else if ($part[0] === '&' && substr($part, -1) === ';' && $part !== '&lt;' && $part !== '&gt;' && $part !== '&amp;') {
$output .= html_entity_decode($part); // Evaluate HTML entit(y|ies)
} else {
$output .= preg_replace('#\s+#', ' ', $part);
}
$prev = $part;
}
$output = str_replace(' </', '</', $output);
// Force space with `&#x0020;` and linebreak with `&#x000A;`
return str_ireplace(['&#x0020;', '&#x20;', '&#x000A;', '&#xA;'], [' ', ' ', "\n", "\n"], trim($output));
}
function fn_minify_html_union($input, $quote) {
if (
strpos($input, ' ') === false &&
strpos($input, "\n") === false &&
strpos($input, "\t") === false
) return $input;
global $url;
return preg_replace_callback('#<\s*([^\/\s]+)\s*(?:>|(\s[^<>]+?)\s*>)#', function($m) use($quote, $url) {
if (isset($m[2])) {
// Minify inline CSS(s)
if (stripos($m[2], ' style=') !== false) {
$m[2] = preg_replace_callback('#( style=)([\'"]?)(.*?)\2#i', function($m) {
return $m[1] . $m[2] . fn_minify_css($m[3]) . $m[2];
}, $m[2]);
}
// Minify URL(s)
if (strpos($m[2], '://') !== false) {
// $m[2] = str_replace([
// $url . '/',
// $url . '?',
// $url . '&',
// $url . '#',
// $url . '"',
// $url . "'"
// ], [
// '/',
// '?',
// '&',
// '#',
// '/"',
// "/'"
// ], $m[2]);
}
$a = 'a(sync|uto(focus|play))|c(hecked|ontrols)|d(efer|isabled)|hidden|ismap|loop|multiple|open|re(adonly|quired)|s((cop|elect)ed|pellcheck)';
$a = '<' . $m[1] . preg_replace([
// From `a="a"`, `a='a'`, `a="true"`, `a='true'`, `a=""` and `a=''` to `a` [^1]
'#\s(' . $a . ')(?:=([\'"]?)(?:true|\1)?\2)#i',
// Remove extra whitespace(s) between HTML attribute(s) [^2]
'#\s*([^\s=]+?)(=(?:\S+|([\'"]?).*?\3)|$)#',
// From `<img />` to `<img/>` [^3]
'#\s+\/$#'
], [
// [^1]
' $1',
// [^2]
' $1$2',
// [^3]
'/'
], str_replace("\n", ' ', $m[2])) . '>';
return $quote !== 1 ? fn_minify_html_union_attr($a) : $a;
}
return '<' . $m[1] . '>';
}, $input);
}
function fn_minify_html_union_attr($input) {
if (strpos($input, '=') === false) return $input;
return preg_replace_callback('#=(' . MINIFY_STRING . ')#', function($m) {
$q = $m[1][0];
if (strpos($m[1], ' ') === false && preg_match('#^' . $q . '[a-zA-Z_][\w-]*?' . $q . '$#', $m[1])) {
return '=' . tr($m[1], $q);
}
return $m[0];
}, $input);
}
function fn_minify_js($input, $comment = 2, $quote = 2) {
if (!is_string($input) || !$input = n(trim($input))) return $input;
$output = $prev = "";
foreach (fn_minify([MINIFY_COMMENT_CSS, MINIFY_STRING, MINIFY_COMMENT_JS, MINIFY_PATTERN_JS], $input) as $part) {
if (trim($part) === "") continue;
if ($comment !== 1 && (
strpos($part, '//') === 0 || // Remove inline comment(s)
strpos($part, '/*') === 0 && substr($part, -2) === '*/'
)) {
if (
$comment === 2 && (
// Detect special comment(s) from the third character. It should be a `!` or `*` → `/*! keep */` or `/** keep */`
strpos('*!', $part[2]) !== false ||
// Detect license comment(s) from the content. It should contains character(s) like `@license`
stripos($part, '@licence') !== false || // noun
stripos($part, '@license') !== false || // verb
stripos($part, '@preserve') !== false
)
) {
$output .= $part;
}
continue;
}
if ($part[0] === '/' && (substr($part, -1) === '/' || preg_match('#\/[gimuy]*$#', $part))) {
} else if ($part[0] === '"' && substr($part, -1) === '"' || $part[0] === "'" && substr($part, -1) === "'") {
// TODO: Remove quote(s) where possible …
$output .= $part;
} else {
$output .= fn_minify_js_union($part);
}
$prev = $part;
}
return $output;
}
function fn_minify_js_union($input) {
return preg_replace([
// Remove whitespace(s) around punctuation(s) [^1]
'#\s*([!%&*\(\)\-=+\[\]\{\}|;:,.<>?\/])\s*#',
// Remove the last semicolon and comma [^2]
'#[;,]([\]\}])#',
// Replace `true` with `!0` and `false` with `!1` [^3]
'#\btrue\b#', '#\bfalse\b#', '#\b(return\s?)\s*\b#',
// Replace `new Array(x)` with `[x]` … [^4]
'#\b(?:new\s+)?Array\((.*?)\)#', '#\b(?:new\s+)?Object\((.*?)\)#'
], [
// [^1]
'$1',
// [^2]
'$1',
// [^3]
'!0', '!1', '$1',
// [^4]
'[$1]', '{$1}'
], $input);
}
/**
* Backward Compatibility
* ----------------------
*/
function minify_css(...$lot) {
return fn_minify_css(...$lot);
}
function minify_html(...$lot) {
return fn_minify_html(...$lot);
}
function minify_js(...$lot) {
return fn_minify_js(...$lot);
}