File: /var/www/html/bwcdev/wp-content/plugins/calculated-fields-form/js/JSMin.php
<?php
// phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
// phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
// phpcs:disable Squiz.Commenting.ClassComment.Missing
// phpcs:disable Squiz.Commenting.VariableComment.Missing
// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
/**
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
*
* <code>
* $minifiedJs = JSMin::minify($js);
* </code>
*
* This is a modified port of jsmin.c. Improvements:
*
* Does not choke on some regexp literals containing quote characters. E.g. /'/
*
* Spaces are preserved after some add/sub operators, so they are not mistakenly
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
*
* Preserves multi-line comments that begin with /*!
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com> (PHP port)
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
const ACTION_KEEP_A = 1;
const ACTION_DELETE_A = 2;
const ACTION_DELETE_A_B = 3;
protected $a = "\n";
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
protected $lastByteOut = '';
protected $keptComment = '';
/**
* Minify Javascript.
*
* @param string $js Javascript to be minified.
*
* @return string
*/
public static function minify( $js ) {
$jsmin = new JSMin( $js );
return $jsmin->min();
}
/**
* Construct
*
* @param string $input JS code to minify.
*/
public function __construct( $input ) {
$this->input = $input;
}
/**
* Perform minification, return result
*
* @return string
*/
public function min() {
if ( '' !== $this->output ) { // min already run.
return $this->output;
}
$mbIntEnc = null;
$this->input = str_replace( "\r\n", "\n", $this->input );
$this->inputLength = strlen( $this->input );
$this->action( self::ACTION_DELETE_A_B );
while ( null !== $this->a ) {
// determine next command.
$command = self::ACTION_KEEP_A; // default.
if ( ' ' === $this->a ) {
if ( ( '+' === $this->lastByteOut || '-' === $this->lastByteOut ) && $this->b === $this->lastByteOut ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement
// Don't delete this space. If we do, the addition/subtraction.
// could be parsed as a post-increment.
} elseif ( ! $this->isAlphaNum( $this->b ) ) {
$command = self::ACTION_DELETE_A;
}
} elseif ( "\n" === $this->a ) {
if ( ' ' === $this->b ) {
$command = self::ACTION_DELETE_A_B;
} elseif ( null === $this->b
|| ( false === strpos( '{[(+-!~', $this->b )
&& ! $this->isAlphaNum( $this->b ) ) ) {
$command = self::ACTION_DELETE_A;
}
} elseif ( ! $this->isAlphaNum( $this->a ) ) {
if ( ' ' === $this->b
|| ( "\n" === $this->b
&& ( false === strpos( '}])+-"\'', $this->a ) ) ) ) {
$command = self::ACTION_DELETE_A_B;
}
}
$this->action( $command );
}
$this->output = trim( $this->output );
if ( null !== $mbIntEnc ) {
mb_internal_encoding( $mbIntEnc );
}
return $this->output;
}
/**
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
* ACTION_DELETE_A = Copy B to A. Get the next B.
* ACTION_DELETE_A_B = Get the next B.
*
* @param int $command command id.
* @throws JSMin_UnterminatedRegExpException Possible method error.
* @throws JSMin_UnterminatedStringException Possible method error.
*/
protected function action( $command ) {
// make sure we don't compress "a + ++b" to "a+++b", etc.
if ( self::ACTION_DELETE_A_B === $command
&& ' ' === $this->b
&& ( '+' === $this->a || '-' === $this->a ) ) {
// Note: we're at an addition/substraction operator; the inputIndex.
// will certainly be a valid index.
if ( $this->input[ $this->inputIndex ] === $this->a ) {
// This is "+ +" or "- -". Don't delete the space.
$command = self::ACTION_KEEP_A;
}
}
switch ( $command ) {
case self::ACTION_KEEP_A: // 1.
$this->output .= $this->a;
if ( $this->keptComment ) {
$this->output = rtrim( $this->output, "\n" );
$this->output .= $this->keptComment;
$this->keptComment = '';
}
$this->lastByteOut = $this->a;
// fallthrough intentional.
case self::ACTION_DELETE_A: // 2.
$this->a = $this->b;
if ( "'" === $this->a || '"' === $this->a ) { // string literal.
$str = $this->a; // in case needed for exception.
for ( ;; ) {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
if ( $this->a === $this->b ) { // end quote.
break;
}
if ( $this->isEOF( $this->a ) ) {
throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte {$this->inputIndex}: {$str}" // phpcs:ignore
);
}
$str .= $this->a;
if ( '\\' === $this->a ) {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
$str .= $this->a;
}
}
}
// fallthrough intentional.
case self::ACTION_DELETE_A_B: // 3.
$this->b = $this->next();
if ( '/' === $this->b && $this->isRegexpLiteral() ) {
$this->output .= $this->a . $this->b;
$pattern = '/'; // keep entire pattern in case we need to report it in the exception.
for ( ;; ) {
$this->a = $this->get();
$pattern .= $this->a;
if ( '[' === $this->a ) {
for ( ;; ) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
if ( ']' === $this->a ) {
break;
}
if ( '\\' === $this->a ) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
}
if ( $this->isEOF( $this->a ) ) {
throw new JSMin_UnterminatedRegExpException(
'JSMin: Unterminated set in RegExp at byte '
. $this->inputIndex . ": {$pattern}" // phpcs:ignore
);
}
}
}
if ( '/' === $this->a ) { // end pattern.
break; // while (true).
} elseif ( '\\' === $this->a ) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
} elseif ( $this->isEOF( $this->a ) ) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}" // phpcs:ignore
);
}
$this->output .= $this->a;
$this->lastByteOut = $this->a;
}
$this->b = $this->next();
}
// end case ACTION_DELETE_A_B.
}
}
/**
* Check regular expressions.
*
* @return bool
*/
protected function isRegexpLiteral() {
if ( false !== strpos( '(,=:[!&|?+-~*{;', $this->a ) ) {
// we obviously aren't dividing.
return true;
}
if ( ' ' === $this->a || "\n" === $this->a ) {
$length = strlen( $this->output );
if ( $length < 2 ) { // weird edge case.
return true;
}
// you can't divide a keyword.
if ( preg_match( '/(?:case|else|in|return|typeof)$/', $this->output, $m ) ) {
if ( $this->output === $m[0] ) { // odd but could happen.
return true;
}
// make sure it's a keyword, not end of an identifier.
$charBeforeKeyword = substr( $this->output, $length - strlen( $m[0] ) - 1, 1 );
if ( ! $this->isAlphaNum( $charBeforeKeyword ) ) {
return true;
}
}
}
return false;
}
/**
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
* translate it to a space or linefeed.
*
* @return string
*/
protected function get() {
$c = $this->lookAhead;
$this->lookAhead = null;
if ( null === $c ) {
// getc(stdin).
if ( $this->inputIndex < $this->inputLength ) {
$c = $this->input[ $this->inputIndex ];
$this->inputIndex += 1;
} else {
$c = null;
}
}
if ( null === $c || ord( $c ) >= self::ORD_SPACE || "\n" === $c ) {
return $c;
}
if ( "\r" === $c ) {
return "\n";
}
return ' ';
}
/**
* Does $a indicate end of input?
*
* @param string $a text to check.
* @return bool
*/
protected function isEOF( $a ) {
return ord( $a ) <= self::ORD_LF;
}
/**
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
*
* @return string
*/
protected function peek() {
$this->lookAhead = $this->get();
return $this->lookAhead;
}
/**
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
*
* @param string $c text to check.
*
* @return bool
*/
protected function isAlphaNum( $c ) {
return ( preg_match( '/^[a-z0-9A-Z_\\$\\\\]$/', $c ) || ord( $c ) > 126 );
}
/**
* Consume a single line comment from input (possibly retaining it)
*/
protected function consumeSingleLineComment() {
$comment = '';
while ( true ) {
$get = $this->get();
$comment .= $get;
if ( ord( $get ) <= self::ORD_LF ) { // end of line reached.
// if IE conditional comment.
if ( preg_match( '/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment ) ) {
$this->keptComment .= "/{$comment}";
}
return;
}
}
}
/**
* Consume a multiple line comment from input (possibly retaining it)
*
* @throws JSMin_UnterminatedCommentException Possible method error.
*/
protected function consumeMultipleLineComment() {
$this->get();
$comment = '';
for ( ;; ) {
$get = $this->get();
if ( '*' === $get ) {
if ( '/' === $this->peek() ) { // end of comment reached.
$this->get();
if ( 0 === strpos( $comment, '!' ) ) {
// preserved by YUI Compressor.
if ( ! $this->keptComment ) {
// don't prepend a newline if two comments right after one another.
$this->keptComment = "\n";
}
$this->keptComment .= '/*!' . substr( $comment, 1 ) . "*/\n";
} elseif ( preg_match( '/^@(?:cc_on|if|elif|else|end)\\b/', $comment ) ) {
// IE conditional.
$this->keptComment .= "/*{$comment}*/";
}
return;
}
} elseif ( null === $get ) {
throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}" // phpcs:ignore
);
}
$comment .= $get;
}
}
/**
* Get the next character, skipping over comments. Some comments may be preserved.
*
* @return string
*/
protected function next() {
$get = $this->get();
if ( '/' === $get ) {
switch ( $this->peek() ) {
case '/':
$this->consumeSingleLineComment();
$get = "\n";
break;
case '*':
$this->consumeMultipleLineComment();
$get = ' ';
break;
}
}
return $get;
}
}
class JSMin_UnterminatedStringException extends Exception {}
class JSMin_UnterminatedCommentException extends Exception {}
class JSMin_UnterminatedRegExpException extends Exception {}