Merge branch 'MDL-58550-master' of git://github.com/ankitagarwal/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Mon, 2 Oct 2017 03:57:34 +0000 (11:57 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Mon, 2 Oct 2017 03:57:34 +0000 (11:57 +0800)
lib/minify/matthiasmullie-minify/data/js/keywords_before.txt
lib/minify/matthiasmullie-minify/data/js/operators_after.txt
lib/minify/matthiasmullie-minify/src/CSS.php
lib/minify/matthiasmullie-minify/src/JS.php
lib/minify/matthiasmullie-minify/src/Minify.php
lib/minify/matthiasmullie-pathconverter/src/Converter.php
lib/minify/matthiasmullie-pathconverter/src/ConverterInterface.php [new file with mode: 0644]
lib/minify/matthiasmullie-pathconverter/src/NoConverter.php [new file with mode: 0644]
lib/minify/readme_moodle.txt
lib/thirdpartylibs.xml

index f9c359c..ec96ebc 100644 (file)
@@ -3,6 +3,7 @@
 namespace MatthiasMullie\Minify;
 
 use MatthiasMullie\Minify\Exceptions\FileImportException;
+use MatthiasMullie\PathConverter\ConverterInterface;
 use MatthiasMullie\PathConverter\Converter;
 
 /**
@@ -12,7 +13,7 @@ use MatthiasMullie\PathConverter\Converter;
  *
  * @author Matthias Mullie <minify@mullie.eu>
  * @author Tijs Verkoyen <minify@verkoyen.eu>
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
  * @license MIT License
  */
 class CSS extends Minify
@@ -83,7 +84,7 @@ class CSS extends Minify
 
             // add to top
             $content = implode('', $matches[0]).$content;
-        };
+        }
 
         return $content;
     }
@@ -94,9 +95,9 @@ class CSS extends Minify
      * @import's will be loaded and their content merged into the original file,
      * to save HTTP requests.
      *
-     * @param string   $source  The file to combine imports for.
-     * @param string   $content The CSS content to combine imports for.
-     * @param string[] $parents Parent paths, for circular reference checks.
+     * @param string   $source  The file to combine imports for
+     * @param string   $content The CSS content to combine imports for
+     * @param string[] $parents Parent paths, for circular reference checks
      *
      * @return string
      *
@@ -120,16 +121,7 @@ class CSS extends Minify
                     (?P<quotes>["\']?)
 
                         # fetch path
-                        (?P<path>
-
-                            # do not fetch data uris or external sources
-                            (?!(
-                                ["\']?
-                                (data|https?):
-                            ))
-
-                            .+?
-                        )
+                        (?P<path>.+?)
 
                     # (optional) close path enclosure
                     (?P=quotes)
@@ -164,16 +156,7 @@ class CSS extends Minify
                 (?P<quotes>["\'])
 
                     # fetch path
-                    (?P<path>
-
-                        # do not fetch data uris or external sources
-                        (?!(
-                            ["\']?
-                            (data|https?):
-                        ))
-
-                        .+?
-                    )
+                    (?P<path>.+?)
 
                 # close path enclosure
                 (?P=quotes)
@@ -211,33 +194,33 @@ class CSS extends Minify
 
             // only replace the import with the content if we can grab the
             // content of the file
-            if ($this->canImportFile($importPath)) {
-                // check if current file was not imported previously in the same
-                // import chain.
-                if (in_array($importPath, $parents)) {
-                    throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
-                }
+            if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
+                continue;
+            }
 
-                // grab referenced file & minify it (which may include importing
-                // yet other @import statements recursively)
-                $minifier = new static($importPath);
-                $importContent = $minifier->execute($source, $parents);
+            // check if current file was not imported previously in the same
+            // import chain.
+            if (in_array($importPath, $parents)) {
+                throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
+            }
 
-                // check if this is only valid for certain media
-                if (!empty($match['media'])) {
-                    $importContent = '@media '.$match['media'].'{'.$importContent.'}';
-                }
+            // grab referenced file & minify it (which may include importing
+            // yet other @import statements recursively)
+            $minifier = new static($importPath);
+            $importContent = $minifier->execute($source, $parents);
 
-                // add to replacement array
-                $search[] = $match[0];
-                $replace[] = $importContent;
+            // check if this is only valid for certain media
+            if (!empty($match['media'])) {
+                $importContent = '@media '.$match['media'].'{'.$importContent.'}';
             }
+
+            // add to replacement array
+            $search[] = $match[0];
+            $replace[] = $importContent;
         }
 
         // replace the import statements
-        $content = str_replace($search, $replace, $content);
-
-        return $content;
+        return str_replace($search, $replace, $content);
     }
 
     /**
@@ -246,25 +229,28 @@ class CSS extends Minify
      * @url(image.jpg) images will be loaded and their content merged into the
      * original file, to save HTTP requests.
      *
-     * @param string $source  The file to import files for.
-     * @param string $content The CSS content to import files for.
+     * @param string $source  The file to import files for
+     * @param string $content The CSS content to import files for
      *
      * @return string
      */
     protected function importFiles($source, $content)
     {
-        $extensions = array_keys($this->importExtensions);
-        $regex = '/url\((["\']?)((?!["\']?data:).*?\.('.implode('|', $extensions).'))\\1\)/i';
-        if ($extensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
+        $regex = '/url\((["\']?)(.+?)\\1\)/i';
+        if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
             $search = array();
             $replace = array();
 
             // loop the matches
             foreach ($matches as $match) {
+                $extension = substr(strrchr($match[2], '.'), 1);
+                if ($extension && !array_key_exists($extension, $this->importExtensions)) {
+                    continue;
+                }
+
                 // get the path for the file that will be imported
                 $path = $match[2];
                 $path = dirname($source).'/'.$path;
-                $extension = $match[3];
 
                 // only replace the import with the content if we're able to get
                 // the content of the file, and it's relatively small
@@ -290,21 +276,21 @@ class CSS extends Minify
      * Minify the data.
      * Perform CSS optimizations.
      *
-     * @param string[optional] $path    Path to write the data to.
-     * @param string[]         $parents Parent paths, for circular reference checks.
+     * @param string[optional] $path    Path to write the data to
+     * @param string[]         $parents Parent paths, for circular reference checks
      *
-     * @return string The minified data.
+     * @return string The minified data
      */
     public function execute($path = null, $parents = array())
     {
         $content = '';
 
-        // loop css data (raw data and files)
+        // loop CSS data (raw data and files)
         foreach ($this->data as $source => $css) {
             /*
-             * Let's first take out strings & comments, since we can't just remove
-             * whitespace anywhere. If whitespace occurs inside a string, we should
-             * leave it alone. E.g.:
+             * Let's first take out strings & comments, since we can't just
+             * remove whitespace anywhere. If whitespace occurs inside a string,
+             * we should leave it alone. E.g.:
              * p { content: "a   test" }
              */
             $this->extractStrings();
@@ -330,9 +316,9 @@ class CSS extends Minify
              * to be relative no longer to the source file, but to the new path.
              * If we don't write to a file, fall back to same path so no
              * conversion happens (because we still want it to go through most
-             * of the move code...)
+             * of the move code, which also addresses url() & @import syntax...)
              */
-            $converter = new Converter($source, $path ?: $source);
+            $converter = $this->getPathConverter($source, $path ?: $source);
             $css = $this->move($converter, $css);
 
             // combine css
@@ -350,12 +336,12 @@ class CSS extends Minify
      * will have to be updated when a file is being saved at another location
      * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
      *
-     * @param Converter $converter Relative path converter
-     * @param string    $content   The CSS content to update relative urls for.
+     * @param ConverterInterface $converter Relative path converter
+     * @param string             $content   The CSS content to update relative urls for
      *
      * @return string
      */
-    protected function move(Converter $converter, $content)
+    protected function move(ConverterInterface $converter, $content)
     {
         /*
          * Relative path references will usually be enclosed by url(). @import
@@ -380,17 +366,7 @@ class CSS extends Minify
                 (?P<quotes>["\'])?
 
                     # fetch path
-                    (?P<path>
-
-                        # do not fetch data uris or external sources
-                        (?!(
-                            \s?
-                            ["\']?
-                            (data|https?):
-                        ))
-
-                        .+?
-                    )
+                    (?P<path>.+?)
 
                 # close path enclosure
                 (?(quotes)(?P=quotes))
@@ -417,16 +393,7 @@ class CSS extends Minify
                 (?P<quotes>["\'])
 
                     # fetch path
-                    (?P<path>
-
-                        # do not fetch data uris or external sources
-                        (?!(
-                            ["\']?
-                            (data|https?):
-                        ))
-
-                        .+?
-                    )
+                    (?P<path>.+?)
 
                 # close path enclosure
                 (?P=quotes)
@@ -450,42 +417,61 @@ class CSS extends Minify
             // determine if it's a url() or an @import match
             $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
 
-            // attempting to interpret GET-params makes no sense, so let's discard them for awhile
-            $params = strrchr($match['path'], '?');
-            $url = $params ? substr($match['path'], 0, -strlen($params)) : $match['path'];
+            $url = $match['path'];
+            if ($this->canImportByPath($url)) {
+                // attempting to interpret GET-params makes no sense, so let's discard them for awhile
+                $params = strrchr($url, '?');
+                $url = $params ? substr($url, 0, -strlen($params)) : $url;
 
-            // fix relative url
-            $url = $converter->convert($url);
+                // fix relative url
+                $url = $converter->convert($url);
 
-            // now that the path has been converted, re-apply GET-params
-            $url .= $params;
+                // now that the path has been converted, re-apply GET-params
+                $url .= $params;
+            }
+
+            /*
+             * Urls with control characters above 0x7e should be quoted.
+             * According to Mozilla's parser, whitespace is only allowed at the
+             * end of unquoted urls.
+             * Urls with `)` (as could happen with data: uris) should also be
+             * quoted to avoid being confused for the url() closing parentheses.
+             * And urls with a # have also been reported to cause issues.
+             * Urls with quotes inside should also remain escaped.
+             *
+             * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
+             * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
+             * @see https://github.com/matthiasmullie/minify/issues/193
+             */
+            $url = trim($url);
+            if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
+                $url = $match['quotes'] . $url . $match['quotes'];
+            }
 
             // build replacement
             $search[] = $match[0];
-            if ($type == 'url') {
+            if ($type === 'url') {
                 $replace[] = 'url('.$url.')';
-            } elseif ($type == 'import') {
+            } elseif ($type === 'import') {
                 $replace[] = '@import "'.$url.'"';
             }
         }
 
         // replace urls
-        $content = str_replace($search, $replace, $content);
-
-        return $content;
+        return str_replace($search, $replace, $content);
     }
 
     /**
      * Shorthand hex color codes.
      * #FF0000 -> #F00.
      *
-     * @param string $content The CSS content to shorten the hex color codes for.
+     * @param string $content The CSS content to shorten the hex color codes for
      *
      * @return string
      */
     protected function shortenHex($content)
     {
-        $content = preg_replace('/(?<![\'"])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?![\'"])/i', '#$1$2$3', $content);
+        $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?=[; }])/i', '#$1$2$3', $content);
 
         // we can shorten some even more by replacing them with their color name
         $colors = array(
@@ -518,13 +504,19 @@ class CSS extends Minify
             '#F5DEB3' => 'wheat',
         );
 
-        return str_ireplace(array_keys($colors), $colors, $content);
+        return preg_replace_callback(
+            '/(?<=[: ])('.implode(array_keys($colors), '|').')(?=[; }])/i',
+            function ($match) use ($colors) {
+                return $colors[strtoupper($match[0])];
+            },
+            $content
+        );
     }
 
     /**
      * Shorten CSS font weights.
      *
-     * @param string $content The CSS content to shorten the font weights for.
+     * @param string $content The CSS content to shorten the font weights for
      *
      * @return string
      */
@@ -536,7 +528,7 @@ class CSS extends Minify
         );
 
         $callback = function ($match) use ($weights) {
-            return $match[1] . $weights[$match[2]];
+            return $match[1].$weights[$match[2]];
         };
 
         return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
@@ -545,7 +537,7 @@ class CSS extends Minify
     /**
      * Shorthand 0 values to plain 0, instead of e.g. -0em.
      *
-     * @param string $content The CSS content to shorten the zero values for.
+     * @param string $content The CSS content to shorten the zero values for
      *
      * @return string
      */
@@ -585,23 +577,29 @@ class CSS extends Minify
         // looped because there may be multiple 0s inside 1 group of parentheses
         do {
             $previous = $content;
-            $content = preg_replace('/\(([^\(\)]+)\s+[\+\-]\s+0(\s+[^\(\)]+)?\)/', '(\\1\\2)', $content);
-        } while ( $content !== $previous );
+            $content = preg_replace('/\(([^\(\)]+) [\+\-] 0( [^\(\)]+)?\)/', '(\\1\\2)', $content);
+        } while ($content !== $previous);
         // strip all `0 +` occurrences: calc(0 + 10%) -> calc(10%)
-        $content = preg_replace('/\(\s*0\s+\+\s+([^\(\)]+)\)/', '(\\1)', $content);
+        $content = preg_replace('/\(0 \+ ([^\(\)]+)\)/', '(\\1)', $content);
         // strip all `0 -` occurrences: calc(0 - 10%) -> calc(-10%)
-        $content = preg_replace('/\(\s*0\s+\-\s+([^\(\)]+)\)/', '(-\\1)', $content);
+        $content = preg_replace('/\(0 \- ([^\(\)]+)\)/', '(-\\1)', $content);
         // I'm not going to attempt to optimize away `x * 0` instances:
         // it's dumb enough code already that it likely won't occur, and it's
         // too complex to do right (order of operations would have to be
         // respected etc)
         // what I cared about most here was fixing incorrectly truncated units
 
+        // IE doesn't seem to understand a unitless flex-basis value, so let's
+        // add it in again (make it `%`, which is only 1 char: 0%, 0px, 0
+        // anything, it's all just the same)
+        $content = preg_replace('/flex:([^ ]+ [^ ]+ )0([;\}])/', 'flex:${1}0%${2}', $content);
+        $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
+
         return $content;
     }
 
     /**
-     * Strip comments from source code.
+     * Strip empty tags from source code.
      *
      * @param string $content
      *
@@ -609,7 +607,7 @@ class CSS extends Minify
      */
     protected function stripEmptyTags($content)
     {
-        return preg_replace('/(^|\})[^\{\}]+\{\s*\}/', '\\1', $content);
+        return preg_replace('/(^|\}|;)[^\{\};]+\{\s*\}/', '\\1', $content);
     }
 
     /**
@@ -623,7 +621,7 @@ class CSS extends Minify
     /**
      * Strip whitespace.
      *
-     * @param string $content The CSS content to strip the whitespace for.
+     * @param string $content The CSS content to strip the whitespace for
      *
      * @return string
      */
@@ -643,10 +641,12 @@ class CSS extends Minify
         $content = preg_replace('/\s+([\]\)])/', '$1', $content);
         $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
 
-        // whitespace around + and - can only be stripped in selectors, like
-        // :nth-child(3+2n), not in things like calc(3px + 2px) or shorthands
-        // like 3px -2px
-        $content = preg_replace('/\s*([+-])\s*(?=[^}]*{)/', '$1', $content);
+        // whitespace around + and - can only be stripped inside some pseudo-
+        // classes, like `:nth-child(3+2n)`
+        // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
+        // selectors like `div.weird- p`
+        $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
+        $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
 
         // remove semicolon/whitespace followed by closing bracket
         $content = str_replace(';}', '}', $content);
@@ -657,7 +657,7 @@ class CSS extends Minify
     /**
      * Check if file is small enough to be imported.
      *
-     * @param string $path The path to the file.
+     * @param string $path The path to the file
      *
      * @return bool
      */
@@ -665,4 +665,30 @@ class CSS extends Minify
     {
         return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
     }
+
+    /**
+     * Check if file a file can be imported, going by the path.
+     *
+     * @param string $path
+     *
+     * @return bool
+     */
+    protected function canImportByPath($path)
+    {
+        return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
+    }
+
+    /**
+     * Return a converter to update relative paths to be relative to the new
+     * destination.
+     *
+     * @param string $source
+     * @param string $target
+     *
+     * @return ConverterInterface
+     */
+    protected function getPathConverter($source, $target)
+    {
+        return new Converter($source, $target);
+    }
 }
index 5dfa7d7..042f542 100644 (file)
@@ -9,7 +9,7 @@ namespace MatthiasMullie\Minify;
  *
  * @author Matthias Mullie <minify@mullie.eu>
  * @author Tijs Verkoyen <minify@verkoyen.eu>
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
  * @license MIT License
  */
 class JS extends Minify
@@ -80,8 +80,7 @@ class JS extends Minify
      * them. Some end of lines are not the end of a statement, like with these
      * operators.
      *
-     * Note: Most operators are fine, we've only removed !, ++ and --.
-     * There can't be a newline separating ! and whatever it is negating.
+     * Note: Most operators are fine, we've only removed ++ and --.
      * ++ & -- have to be joined with the value they're in-/decrementing.
      *
      * Will be loaded from /data/js/operators_before.txt
@@ -97,7 +96,8 @@ class JS extends Minify
      * them. Some end of lines are not the end of a statement, like when
      * continued by one of these operators on the newline.
      *
-     * Note: Most operators are fine, we've only removed ), ], ++ and --.
+     * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
+     * There can't be a newline separating ! or ~ and whatever it is negating.
      * ++ & -- have to be joined with the value they're in-/decrementing.
      * ) & ] are "special" in that they have lots or usecases. () for example
      * is used for function calls, for grouping, in if () and for (), ...
@@ -110,6 +110,11 @@ class JS extends Minify
      */
     protected $operatorsAfter = array();
 
+    /**
+     * @var array
+     */
+    protected $nestedExtracted = array();
+
     /**
      * {@inheritdoc}
      */
@@ -131,9 +136,9 @@ class JS extends Minify
      * Minify the data.
      * Perform JS optimizations.
      *
-     * @param string[optional] $path Path to write the data to.
+     * @param string[optional] $path Path to write the data to
      *
-     * @return string The minified data.
+     * @return string The minified data
      */
     public function execute($path = null)
     {
@@ -216,20 +221,75 @@ class JS extends Minify
         $minifier = $this;
         $callback = function ($match) use ($minifier) {
             $count = count($minifier->extracted);
-            $placeholder = '/'.$count.'/';
-            $minifier->extracted[$placeholder] = $match[0];
+            $placeholder = '"'.$count.'"';
+            $minifier->extracted[$placeholder] = $match['regex'];
+
+            // because we're also trying to find regular expressions that follow
+            // if/when/for statements, we should also make sure that the content
+            // within these statements is also minified...
+            // e.g. `if("some   string"/* or comment */)` should become
+            //      `if("some   string")`
+            if (isset($match['before'])) {
+                $other = new static();
+                $other->extractStrings('\'"`', "$count-");
+                $other->stripComments();
+                $match['before'] = $other->replace($match['before']);
+                $this->nestedExtracted += $other->extracted;
+            }
 
-            return $placeholder;
+            return (isset($match['before']) ? $match['before'] : '').
+                $placeholder.
+                (isset($match['after']) ? $match['after'] : '');
         };
 
-        $pattern = '\/.*?(?<!\\\\)(\\\\\\\\)*+\/[gimy]*(?![0-9a-zA-Z\/])';
+        $pattern = '(?P<regex>\/.+?((?<!\\\\)\\\\\\\\)*\/[gimy]*)(?![0-9a-zA-Z\/])';
 
         // a regular expression can only be followed by a few operators or some
         // of the RegExp methods (a `\` followed by a variable or value is
         // likely part of a division, not a regex)
-        $after = '[,;\)\}]';
-        $methods = '\.(exec|test|match|search|replace|split)\(';
-        $this->registerPattern('/'.$pattern.'(?=\s*('.$after.'|'.$methods.'))/', $callback);
+        $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return',  'typeof');
+        $before = '(?P<before>[=:,;\}\(\{&\|!]|^|'.implode('|', $keywords).')';
+        $propertiesAndMethods = array(
+            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties
+            'prototype',
+            'length',
+            'lastIndex',
+            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
+            'constructor',
+            'flags',
+            'global',
+            'ignoreCase',
+            'multiline',
+            'source',
+            'sticky',
+            'unicode',
+            // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
+            'compile(',
+            'exec(',
+            'test(',
+            'match',
+            'replace(',
+            'search(',
+            'split(',
+            'toSource(',
+            'toString(',
+        );
+        $delimiters = array_fill(0, count($propertiesAndMethods), '/');
+        $propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
+        $after = '(?P<after>[\.,;\)\}&\|+]|$|\.('.implode('|', $propertiesAndMethods).'))';
+        $this->registerPattern('/'.$before.'\s*'.$pattern.'\s*'.$after.'/', $callback);
+
+        // we didn't check for regular expressions after `)`, because that is
+        // more often than not not a character where a regex can follow (e.g.
+        // (1+2)/3/4 -> /3/ could be considered a regex, but it's not)
+        // however, after single-line if/while/for, there could very well be a
+        // regex after `)` (e.g. if(true)/regex/)
+        // there is one problem, though: it's (near) impossible to check for
+        // when the if/while/for statement is closed (same amount of closing
+        // brackets as there were opened), so I'll ignore single-line statements
+        // with nested brackets followed by a regex for now...
+        $before = '(?P<before>\b(if|while|for)\s*\((?P<code>[^\(]+?)\))';
+        $this->registerPattern('/'.$before.'\s*'.$pattern.'\s*'.$after.'/', $callback);
 
         // 1 more edge case: a regex can be followed by a lot more operators or
         // keywords if there's a newline (ASI) in between, where the operator
@@ -237,7 +297,25 @@ class JS extends Minify
         // (https://github.com/matthiasmullie/minify/issues/56)
         $operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
         $operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
-        $this->registerPattern('/'.$pattern.'\s*\n?(?=\s*('.implode('|', $operators).'))/', $callback);
+        $after = '(?P<after>\n\s*('.implode('|', $operators).'))';
+        $this->registerPattern('/'.$pattern.'\s*'.$after.'/', $callback);
+    }
+
+    /**
+     * In addition to the regular restore routine, we also need to restore a few
+     * more things that have been extracted as part of the regex extraction...
+     *
+     * {@inheritdoc}
+     */
+    protected function restoreExtractedData($content)
+    {
+        // restore regular extracted stuff
+        $content = parent::restoreExtractedData($content);
+
+        // restore nested stuff from within regex extraction
+        $content = strtr($content, $this->nestedExtracted);
+
+        return $content;
     }
 
     /**
@@ -252,7 +330,7 @@ class JS extends Minify
      * Because it's sometimes hard to tell if a newline is part of a statement
      * that should be terminated or not, we'll just leave some of them alone.
      *
-     * @param string $content The content to strip the whitespace for.
+     * @param string $content The content to strip the whitespace for
      *
      * @return string
      */
@@ -323,14 +401,15 @@ class JS extends Minify
         /*
          * Next, we'll be removing all semicolons where ASI kicks in.
          * for-loops however, can have an empty body (ending in only a
-         * semicolon), like: `for(i=1;i<3;i++);`
+         * semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
          * Here, nothing happens during the loop; it's just used to keep
          * increasing `i`. With that ; omitted, the next line would be expected
          * to be the for-loop's body...
          * I'm going to double that semicolon (if any) so after the next line,
          * which strips semicolons here & there, we're still left with this one.
          */
-        $content = preg_replace('/(for\([^;]*;[^;]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
+        $content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
+        $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
 
         /*
          * We also can't strip empty else-statements. Even though they're
@@ -367,8 +446,8 @@ class JS extends Minify
     protected function getOperatorsForRegex(array $operators, $delimiter = '/')
     {
         // escape operators for use in regex
-        $delimiter = array_fill(0, count($operators), $delimiter);
-        $escaped = array_map('preg_quote', $operators, $delimiter);
+        $delimiters = array_fill(0, count($operators), $delimiter);
+        $escaped = array_map('preg_quote', $operators, $delimiters);
 
         $operators = array_combine($operators, $escaped);
 
@@ -380,7 +459,7 @@ class JS extends Minify
         $operators['.'] = '(?<![0-9]\s)\.';
 
         // don't confuse = with other assignment shortcuts (e.g. +=)
-        $chars = preg_quote('+-*\=<>%&|');
+        $chars = preg_quote('+-*\=<>%&|', $delimiter);
         $operators['='] = '(?<!['.$chars.'])\=';
 
         return $operators;
@@ -479,10 +558,23 @@ class JS extends Minify
      */
     protected function shortenBools($content)
     {
-        $content = preg_replace('/\btrue\b(?!:)/', '!0', $content);
-        $content = preg_replace('/\bfalse\b(?!:)/', '!1', $content);
+        /*
+         * 'true' or 'false' could be used as property names (which may be
+         * followed by whitespace) - we must not replace those!
+         * Since PHP doesn't allow variable-length (to account for the
+         * whitespace) lookbehind assertions, I need to capture the leading
+         * character and check if it's a `.`
+         */
+        $callback = function ($match) {
+            if (trim($match[1]) === '.') {
+                return $match[0];
+            }
+
+            return $match[1].($match[2] === 'true' ? '!0' : '!1');
+        };
+        $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
 
-        // for(;;) is exactly the same as while(true)
+        // for(;;) is exactly the same as while(true), but shorter :)
         $content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
 
         // now make sure we didn't turn any do ... while(true) into do ... for(;;)
index e832a3b..29905b2 100644 (file)
@@ -11,7 +11,7 @@ use Psr\Cache\CacheItemInterface;
  * Please report bugs on https://github.com/matthiasmullie/minify/issues
  *
  * @author Matthias Mullie <minify@mullie.eu>
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
  * @license MIT License
  */
 abstract class Minify
@@ -53,7 +53,9 @@ abstract class Minify
     /**
      * Add a file or straight-up code to be minified.
      *
-     * @param string $data
+     * @param string|string[] $data
+     *
+     * @return static
      */
     public function add($data /* $data = null, ... */)
     {
@@ -65,6 +67,11 @@ abstract class Minify
 
         // this method can be overloaded
         foreach ($args as $data) {
+            if (is_array($data)) {
+                call_user_func_array(array($this, 'add'), $data);
+                continue;
+            }
+
             // redefine var
             $data = (string) $data;
 
@@ -72,17 +79,23 @@ abstract class Minify
             $value = $this->load($data);
             $key = ($data != $value) ? $data : count($this->data);
 
+            // replace CR linefeeds etc.
+            // @see https://github.com/matthiasmullie/minify/pull/139
+            $value = str_replace(array("\r\n", "\r"), "\n", $value);
+
             // store data
             $this->data[$key] = $value;
         }
+
+        return $this;
     }
 
     /**
      * Minify the data & (optionally) saves it to a file.
      *
-     * @param string[optional] $path Path to write the data to.
+     * @param string[optional] $path Path to write the data to
      *
-     * @return string The minified data.
+     * @return string The minified data
      */
     public function minify($path = null)
     {
@@ -99,10 +112,10 @@ abstract class Minify
     /**
      * Minify & gzip the data & (optionally) saves it to a file.
      *
-     * @param string[optional] $path  Path to write the data to.
-     * @param int[optional]    $level Compression level, from 0 to 9.
+     * @param string[optional] $path  Path to write the data to
+     * @param int[optional]    $level Compression level, from 0 to 9
      *
-     * @return string The minified & gzipped data.
+     * @return string The minified & gzipped data
      */
     public function gzip($path = null, $level = 9)
     {
@@ -120,9 +133,9 @@ abstract class Minify
     /**
      * Minify the data & write it to a CacheItemInterface object.
      *
-     * @param CacheItemInterface $item Cache item to write the data to.
+     * @param CacheItemInterface $item Cache item to write the data to
      *
-     * @return CacheItemInterface Cache item with the minifier data.
+     * @return CacheItemInterface Cache item with the minifier data
      */
     public function cache(CacheItemInterface $item)
     {
@@ -135,16 +148,16 @@ abstract class Minify
     /**
      * Minify the data.
      *
-     * @param string[optional] $path Path to write the data to.
+     * @param string[optional] $path Path to write the data to
      *
-     * @return string The minified data.
+     * @return string The minified data
      */
     abstract public function execute($path = null);
 
     /**
      * Load data.
      *
-     * @param string $data Either a path to a file or the content itself.
+     * @param string $data Either a path to a file or the content itself
      *
      * @return string
      */
@@ -166,8 +179,8 @@ abstract class Minify
     /**
      * Save to file.
      *
-     * @param string $content The minified data.
-     * @param string $path    The path to save the minified data to.
+     * @param string $content The minified data
+     * @param string $path    The path to save the minified data to
      *
      * @throws IOException
      */
@@ -183,8 +196,8 @@ abstract class Minify
     /**
      * Register a pattern to execute against the source content.
      *
-     * @param string          $pattern     PCRE pattern.
-     * @param string|callable $replacement Replacement value for matched pattern.
+     * @param string          $pattern     PCRE pattern
+     * @param string|callable $replacement Replacement value for matched pattern
      */
     protected function registerPattern($pattern, $replacement = '')
     {
@@ -202,9 +215,9 @@ abstract class Minify
      * The only way to accurately replace these pieces is to traverse the JS one
      * character at a time and try to find whatever starts first.
      *
-     * @param string $content The content to replace patterns in.
+     * @param string $content The content to replace patterns in
      *
-     * @return string The (manipulated) content.
+     * @return string The (manipulated) content
      */
     protected function replace($content)
     {
@@ -284,9 +297,9 @@ abstract class Minify
      * This function will be called plenty of times, where $content will always
      * move up 1 character.
      *
-     * @param string          $pattern     Pattern to match.
-     * @param string|callable $replacement Replacement value.
-     * @param string          $content     Content to match pattern against.
+     * @param string          $pattern     Pattern to match
+     * @param string|callable $replacement Replacement value
+     * @param string          $content     Content to match pattern against
      *
      * @return string
      */
@@ -311,12 +324,13 @@ abstract class Minify
      * via restoreStrings().
      *
      * @param string[optional] $chars
+     * @param string[optional] $placeholderPrefix
      */
-    protected function extractStrings($chars = '\'"')
+    protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
     {
         // PHP only supports $this inside anonymous functions since 5.4
         $minifier = $this;
-        $callback = function ($match) use ($minifier) {
+        $callback = function ($match) use ($minifier, $placeholderPrefix) {
             // check the second index here, because the first always contains a quote
             if ($match[2] === '') {
                 /*
@@ -329,7 +343,7 @@ abstract class Minify
             }
 
             $count = count($minifier->extracted);
-            $placeholder = $match[1].$count.$match[1];
+            $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
             $minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
 
             return $placeholder;
@@ -388,9 +402,9 @@ abstract class Minify
     /**
      * Attempts to open file specified by $path for writing.
      *
-     * @param string $path The path to the file.
+     * @param string $path The path to the file
      *
-     * @return resource Specifier for the target file.
+     * @return resource Specifier for the target file
      *
      * @throws IOException
      */
@@ -406,9 +420,9 @@ abstract class Minify
     /**
      * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
      *
-     * @param resource $handler The resource to write to.
-     * @param string   $content The content to write.
-     * @param string   $path    The path to the file (for exception printing only).
+     * @param resource $handler The resource to write to
+     * @param string   $content The content to write
+     * @param string   $path    The path to the file (for exception printing only)
      *
      * @throws IOException
      */
index f5ee3ae..c8b0c69 100644 (file)
@@ -13,10 +13,10 @@ namespace MatthiasMullie\PathConverter;
  * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
  *
  * @author Matthias Mullie <pathconverter@mullie.eu>
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
  * @license MIT License
  */
-class Converter
+class Converter implements ConverterInterface
 {
     /**
      * @var string
@@ -129,9 +129,9 @@ class Converter
      *     ../../core/layout/images/img.gif relative to
      *     /home/forkcms/frontend/cache/minified_css
      *
-     * @param string $path The relative path that needs to be converted.
+     * @param string $path The relative path that needs to be converted
      *
-     * @return string The new relative path.
+     * @return string The new relative path
      */
     public function convert($path)
     {
@@ -167,7 +167,7 @@ class Converter
      *
      * @return string
      */
-    public function dirname($path)
+    protected function dirname($path)
     {
         if (is_file($path)) {
             return dirname($path);
diff --git a/lib/minify/matthiasmullie-pathconverter/src/ConverterInterface.php b/lib/minify/matthiasmullie-pathconverter/src/ConverterInterface.php
new file mode 100644 (file)
index 0000000..dc1b765
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Convert file paths.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+interface ConverterInterface
+{
+    /**
+     * Convert file paths.
+     *
+     * @param string $path The path to be converted
+     *
+     * @return string The new path
+     */
+    public function convert($path);
+}
diff --git a/lib/minify/matthiasmullie-pathconverter/src/NoConverter.php b/lib/minify/matthiasmullie-pathconverter/src/NoConverter.php
new file mode 100644 (file)
index 0000000..2fcfd0f
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Don't convert paths.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class NoConverter implements ConverterInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function convert($path)
+    {
+        return $path;
+    }
+}
index 4cae7de..e0785d0 100644 (file)
@@ -1,10 +1,10 @@
 Description of MatthiasMullie\Minify import into Moodle
 
-1) Download https://github.com/matthiasmullie/minify/archive/1.3.37.zip
+1) Download https://github.com/matthiasmullie/minify/archive/1.3.51.zip and unzip
 
-mv minify-1.3.37/src /path/to/moodle/lib/minify/matthiasmullie-minify/
-mv minify-1.3.37/data /path/to/moodle/lib/lib/minify/matthiasmullie-minify/
+mv minify-1.3.51/src /path/to/moodle/lib/minify/matthiasmullie-minify/
+mv minify-1.3.51/data /path/to/moodle/lib/minify/matthiasmullie-minify/
 
-2) Download https://github.com/matthiasmullie/path-converter/archive/1.0.8.zip
+2) Download https://github.com/matthiasmullie/path-converter/archive/1.1.0.zip and unzip
 
-mv path-converter-1.0.8/src/ /path/to/moodle/lib/minify/matthiasmullie-pathconverter/
+mv path-converter-1.1.0/src/ /path/to/moodle/lib/minify/matthiasmullie-pathconverter/
index 5d03739..b0acd06 100644 (file)
     <location>minify/matthiasmullie-minify</location>
     <name>MatthiasMullie\Minify</name>
     <license>MIT</license>
-    <version>1.3.37</version>
+    <version>1.3.51</version>
     <licenseversion></licenseversion>
   </library>
   <library>
     <location>minify/matthiasmullie-pathconverter</location>
     <name>MatthiasMullie\PathConverter</name>
     <license>MIT</license>
-    <version>1.0.8</version>
+    <version>1.1.0</version>
     <licenseversion></licenseversion>
   </library>
   <library>