From 1d8828643a5406bcdb3deb315263934aeee75864 Mon Sep 17 00:00:00 2001 From: Sascha Greuel Date: Tue, 27 May 2014 20:08:19 +0200 Subject: [PATCH] Updated to 0.4.0 Also re-implemented the "Work-around for superfluous brackets" (cba23a28c36273ed11e53bea5c66e6f7f33ed4ea) --- .../files/lib/system/style/lessc.inc.php | 560 ++++++++++++++---- 1 file changed, 438 insertions(+), 122 deletions(-) diff --git a/wcfsetup/install/files/lib/system/style/lessc.inc.php b/wcfsetup/install/files/lib/system/style/lessc.inc.php index c33ce56ad4..478302c1ce 100644 --- a/wcfsetup/install/files/lib/system/style/lessc.inc.php +++ b/wcfsetup/install/files/lib/system/style/lessc.inc.php @@ -1,7 +1,7 @@ findImport($url); + if ($realPath === null) return false; if ($this->importDisabled) { return array(false, "/* import disabled */"); } + if (isset($this->allParsedFiles[realpath($realPath)])) { + return array(false, null); + } + $this->addParsedFile($realPath); $parser = $this->makeParser($realPath); $root = $parser->parse(file_get_contents($realPath)); @@ -276,6 +283,8 @@ class lessc { foreach ($this->sortProps($block->props) as $prop) { $this->compileProp($prop, $block, $out); } + + $out->lines = array_values(array_unique($out->lines)); } protected function sortProps($props, $split = false) { @@ -327,6 +336,9 @@ class lessc { $parts[] = "($q[1])"; } break; + case "variable": + $parts[] = $this->compileValue($this->reduce($q)); + break; } } @@ -434,7 +446,7 @@ class lessc { foreach ($selectors as $s) { if (is_array($s)) { list(, $value) = $s; - $out[] = $this->compileValue($this->reduce($value)); + $out[] = trim($this->compileValue($this->reduce($value))); } else { $out[] = $s; } @@ -447,7 +459,7 @@ class lessc { return $left == $right; } - protected function patternMatch($block, $callingArgs) { + protected function patternMatch($block, $orderedArgs, $keywordArgs) { // match the guards if it has them // any one of the groups must have all its guards pass for a match if (!empty($block->guards)) { @@ -455,7 +467,7 @@ class lessc { foreach ($block->guards as $guardGroup) { foreach ($guardGroup as $guard) { $this->pushEnv(); - $this->zipSetArgs($block->args, $callingArgs); + $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs); $negate = false; if ($guard[0] == "negate") { @@ -484,24 +496,34 @@ class lessc { } } - $numCalling = count($callingArgs); - if (empty($block->args)) { - return $block->isVararg || $numCalling == 0; + return $block->isVararg || empty($orderedArgs) && empty($keywordArgs); + } + + $remainingArgs = $block->args; + if ($keywordArgs) { + $remainingArgs = array(); + foreach ($block->args as $arg) { + if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) { + continue; + } + + $remainingArgs[] = $arg; + } } $i = -1; // no args // try to match by arity or by argument literal - foreach ($block->args as $i => $arg) { + foreach ($remainingArgs as $i => $arg) { switch ($arg[0]) { case "lit": - if (empty($callingArgs[$i]) || !$this->eq($arg[1], $callingArgs[$i])) { + if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) { return false; } break; case "arg": // no arg and no default value - if (!isset($callingArgs[$i]) && !isset($arg[2])) { + if (!isset($orderedArgs[$i]) && !isset($arg[2])) { return false; } break; @@ -516,14 +538,19 @@ class lessc { } else { $numMatched = $i + 1; // greater than becuase default values always match - return $numMatched >= $numCalling; + return $numMatched >= count($orderedArgs); } } - protected function patternMatchAll($blocks, $callingArgs) { + protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip=array()) { $matches = null; foreach ($blocks as $block) { - if ($this->patternMatch($block, $callingArgs)) { + // skip seen blocks that don't have arguments + if (isset($skip[$block->id]) && !isset($block->args)) { + continue; + } + + if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) { $matches[] = $block; } } @@ -532,7 +559,7 @@ class lessc { } // attempt to find blocks matched by path and args - protected function findBlocks($searchIn, $path, $args, $seen=array()) { + protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen=array()) { if ($searchIn == null) return null; if (isset($seen[$searchIn->id])) return null; $seen[$searchIn->id] = true; @@ -542,7 +569,7 @@ class lessc { if (isset($searchIn->children[$name])) { $blocks = $searchIn->children[$name]; if (count($path) == 1) { - $matches = $this->patternMatchAll($blocks, $args); + $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen); if (!empty($matches)) { // This will return all blocks that match in the closest // scope that has any matching block, like lessjs @@ -552,7 +579,7 @@ class lessc { $matches = array(); foreach ($blocks as $subBlock) { $subMatches = $this->findBlocks($subBlock, - array_slice($path, 1), $args, $seen); + array_slice($path, 1), $orderedArgs, $keywordArgs, $seen); if (!is_null($subMatches)) { foreach ($subMatches as $sm) { @@ -564,39 +591,51 @@ class lessc { return count($matches) > 0 ? $matches : null; } } - if ($searchIn->parent === $searchIn) return null; - return $this->findBlocks($searchIn->parent, $path, $args, $seen); + return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen); } // sets all argument names in $args to either the default value // or the one passed in through $values - protected function zipSetArgs($args, $values) { - $i = 0; + protected function zipSetArgs($args, $orderedValues, $keywordValues) { $assignedValues = array(); - foreach ($args as $a) { + + $i = 0; + foreach ($args as $a) { if ($a[0] == "arg") { - if ($i < count($values) && !is_null($values[$i])) { - $value = $values[$i]; + if (isset($keywordValues[$a[1]])) { + // has keyword arg + $value = $keywordValues[$a[1]]; + } elseif (isset($orderedValues[$i])) { + // has ordered arg + $value = $orderedValues[$i]; + $i++; } elseif (isset($a[2])) { + // has default value $value = $a[2]; - } else $value = null; + } else { + $this->throwError("Failed to assign arg " . $a[1]); + $value = null; // :( + } $value = $this->reduce($value); $this->set($a[1], $value); $assignedValues[] = $value; + } else { + // a lit + $i++; } - $i++; } // check for a rest $last = end($args); if ($last[0] == "rest") { - $rest = array_slice($values, count($args) - 1); + $rest = array_slice($orderedValues, count($args) - 1); $this->set($last[1], $this->reduce(array("list", " ", $rest))); } - $this->env->arguments = $assignedValues; + // wow is this the only true use of PHP's + operator for arrays? + $this->env->arguments = $assignedValues + $orderedValues; } // compile a prop and update $lines or $blocks appropriately @@ -621,8 +660,28 @@ class lessc { case 'mixin': list(, $path, $args, $suffix) = $prop; - $args = array_map(array($this, "reduce"), (array)$args); - $mixins = $this->findBlocks($block, $path, $args); + $orderedArgs = array(); + $keywordArgs = array(); + foreach ((array)$args as $arg) { + $argval = null; + switch ($arg[0]) { + case "arg": + if (!isset($arg[2])) { + $orderedArgs[] = $this->reduce(array("variable", $arg[1])); + } else { + $keywordArgs[$arg[1]] = $this->reduce($arg[2]); + } + break; + + case "lit": + $orderedArgs[] = $this->reduce($arg[1]); + break; + default: + $this->throwError("Unknown arg type: " . $arg[0]); + } + } + + $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs); if ($mixins === null) { // fwrite(STDERR,"failed to find block: ".implode(" > ", $path)."\n"); @@ -630,6 +689,10 @@ class lessc { } foreach ($mixins as $mixin) { + if ($mixin === $block && !$orderedArgs) { + continue; + } + $haveScope = false; if (isset($mixin->parent->scope)) { $haveScope = true; @@ -641,7 +704,7 @@ class lessc { if (isset($mixin->args)) { $haveArgs = true; $this->pushEnv(); - $this->zipSetArgs($mixin->args, $args); + $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs); } $oldParent = $mixin->parent; @@ -698,7 +761,9 @@ class lessc { list(,$importId) = $prop; $import = $this->env->imports[$importId]; if ($import[0] === false) { - $out->lines[] = $import[1]; + if (isset($import[1])) { + $out->lines[] = $import[1]; + } } else { list(, $bottom, $parser, $importDir) = $import; $this->compileImportedProps($bottom, $block, $out, $parser, $importDir); @@ -786,6 +851,60 @@ class lessc { } } + protected function lib_pow($args) { + list($base, $exp) = $this->assertArgs($args, 2, "pow"); + return pow($this->assertNumber($base), $this->assertNumber($exp)); + } + + protected function lib_pi() { + return pi(); + } + + protected function lib_mod($args) { + list($a, $b) = $this->assertArgs($args, 2, "mod"); + return $this->assertNumber($a) % $this->assertNumber($b); + } + + protected function lib_tan($num) { + return tan($this->assertNumber($num)); + } + + protected function lib_sin($num) { + return sin($this->assertNumber($num)); + } + + protected function lib_cos($num) { + return cos($this->assertNumber($num)); + } + + protected function lib_atan($num) { + $num = atan($this->assertNumber($num)); + return array("number", $num, "rad"); + } + + protected function lib_asin($num) { + $num = asin($this->assertNumber($num)); + return array("number", $num, "rad"); + } + + protected function lib_acos($num) { + $num = acos($this->assertNumber($num)); + return array("number", $num, "rad"); + } + + protected function lib_sqrt($num) { + return sqrt($this->assertNumber($num)); + } + + protected function lib_extract($value) { + list($list, $idx) = $this->assertArgs($value, 2, "extract"); + $idx = $this->assertNumber($idx); + // 1 indexed + if ($list[0] == "list" && isset($list[2][$idx - 1])) { + return $list[2][$idx - 1]; + } + } + protected function lib_isnumber($value) { return $this->toBool($value[0] == "number"); } @@ -814,6 +933,10 @@ class lessc { return $this->toBool($value[0] == "number" && $value[2] == "em"); } + protected function lib_isrem($value) { + return $this->toBool($value[0] == "number" && $value[2] == "rem"); + } + protected function lib_rgbahex($color) { $color = $this->coerceColor($color); if (is_null($color)) @@ -890,6 +1013,16 @@ class lessc { return array("number", round($value), $arg[2]); } + protected function lib_unit($arg) { + if ($arg[0] == "list") { + list($number, $newUnit) = $arg[2]; + return array("number", $this->assertNumber($number), + $this->compileValue($this->lib_e($newUnit))); + } else { + return array("number", $this->assertNumber($arg), ""); + } + } + /** * Helper function to get arguments for color manipulation functions. * takes a list that contains a color like thing and a percentage @@ -996,19 +1129,24 @@ class lessc { } // mixes two colors by weight - // mix(@color1, @color2, @weight); + // mix(@color1, @color2, [@weight: 50%]); // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method protected function lib_mix($args) { - if ($args[0] != "list" || count($args[2]) < 3) + if ($args[0] != "list" || count($args[2]) < 2) $this->throwError("mix expects (color1, color2, weight)"); - list($first, $second, $weight) = $args[2]; + list($first, $second) = $args[2]; $first = $this->assertColor($first); $second = $this->assertColor($second); $first_a = $this->lib_alpha($first); $second_a = $this->lib_alpha($second); - $weight = $weight[1] / 100.0; + + if (isset($args[2][2])) { + $weight = $args[2][2][1] / 100.0; + } else { + $weight = 0.5; + } $w = $weight * 2 - 1; $a = $first_a - $second_a; @@ -1029,6 +1167,25 @@ class lessc { return $this->fixColor($new); } + protected function lib_contrast($args) { + if ($args[0] != 'list' || count($args[2]) < 3) { + return array(array('color', 0, 0, 0), 0); + } + + list($inputColor, $darkColor, $lightColor) = $args[2]; + + $inputColor = $this->assertColor($inputColor); + $darkColor = $this->assertColor($darkColor); + $lightColor = $this->assertColor($lightColor); + $hsl = $this->toHSL($inputColor); + + if ($hsl[3] > 50) { + return $darkColor; + } + + return $lightColor; + } + protected function assertColor($value, $error = "expected color value") { $color = $this->coerceColor($value); if (is_null($color)) $this->throwError($error); @@ -1040,6 +1197,25 @@ class lessc { $this->throwError($error); } + protected function assertArgs($value, $expectedArgs, $name="") { + if ($expectedArgs == 1) { + return $value; + } else { + if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list"); + $values = $value[2]; + $numValues = count($values); + if ($expectedArgs != $numValues) { + if ($name) { + $name = $name . ": "; + } + + $this->throwError("${name}expecting $expectedArgs arguments, got $numValues"); + } + + return $values; + } + } + protected function toHSL($color) { if ($color[0] == 'hsl') return $color; @@ -1091,7 +1267,7 @@ class lessc { * Expects H to be in range of 0 to 360, S and L in 0 to 100 */ protected function toRGB($color) { - if ($color == 'color') return $color; + if ($color[0] == 'color') return $color; $H = $color[1] / 360; $S = $color[2] / 100; @@ -1179,6 +1355,18 @@ class lessc { protected function reduce($value, $forExpression = false) { switch ($value[0]) { + case "interpolate": + $reduced = $this->reduce($value[1]); + $var = $this->compileValue($reduced); + $res = $this->reduce(array("variable", $this->vPrefix . $var)); + + if ($res[0] == "raw_color") { + $res = $this->coerceColor($res); + } + + if (empty($value[2])) $res = $this->lib_e($res); + + return $res; case "variable": $key = $value[1]; if (is_array($key)) { @@ -1299,8 +1487,12 @@ class lessc { case 'keyword': $name = $value[1]; if (isset(self::$cssColors[$name])) { - list($r, $g, $b) = explode(',', self::$cssColors[$name]); - return array('color', $r, $g, $b); + $rgba = explode(',', self::$cssColors[$name]); + + if(isset($rgba[3])) + return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]); + + return array('color', $rgba[0], $rgba[1], $rgba[2]); } return null; } @@ -1445,6 +1637,34 @@ class lessc { return $this->fixColor($out); } + function lib_red($color){ + $color = $this->coerceColor($color); + if (is_null($color)) { + $this->throwError('color expected for red()'); + } + + return $color[1]; + } + + function lib_green($color){ + $color = $this->coerceColor($color); + if (is_null($color)) { + $this->throwError('color expected for green()'); + } + + return $color[2]; + } + + function lib_blue($color){ + $color = $this->coerceColor($color); + if (is_null($color)) { + $this->throwError('color expected for blue()'); + } + + return $color[3]; + } + + // operator on two numbers protected function op_number_number($op, $left, $right) { $unit = empty($left[2]) ? $right[2] : $left[2]; @@ -1605,7 +1825,6 @@ class lessc { $this->importDir = (array)$this->importDir; $this->importDir[] = $pi['dirname'].'/'; - $this->allParsedFiles = array(); $this->addParsedFile($fname); $out = $this->compile(file_get_contents($fname), $fname); @@ -1945,6 +2164,7 @@ class lessc { 'teal' => '0,128,128', 'thistle' => '216,191,216', 'tomato' => '255,99,71', + 'transparent' => '0,0,0,0', 'turquoise' => '64,224,208', 'violet' => '238,130,238', 'wheat' => '245,222,179', @@ -1988,7 +2208,7 @@ class lessc_parser { static protected $supressDivisionProps = array('/border-radius$/i', '/^font$/i'); - protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document"); + protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport"); protected $lineDirectives = array("charset"); /** @@ -2227,7 +2447,7 @@ class lessc_parser { // mixin if ($this->mixinTags($tags) && - ($this->argumentValues($argv) || true) && + ($this->argumentDef($argv, $isVararg) || true) && ($this->keyword($suffix) || true) && $this->end()) { $tags = $this->fixTags($tags); @@ -2519,6 +2739,9 @@ class lessc_parser { $out = array("mediaExp", $feature); if ($value) $out[] = $value; return true; + } elseif ($this->variable($variable)) { + $out = array('variable', $variable); + return true; } $this->seek($s); @@ -2572,12 +2795,10 @@ class lessc_parser { continue; } - if (in_array($tok, $rejectStrs)) { - $count = null; + if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) { break; } - $content[] = $tok; $this->count+= strlen($tok); } @@ -2652,10 +2873,10 @@ class lessc_parser { $s = $this->seek(); if ($this->literal("@{") && - $this->keyword($var) && + $this->openString("}", $interp, null, array("'", '"', ";")) && $this->literal("}", false)) { - $out = array("variable", $this->lessc->vPrefix . $var); + $out = array("interpolate", $interp); $this->eatWhiteDefault = $oldWhite; if ($this->eatWhiteDefault) $this->whitespace(); return true; @@ -2694,38 +2915,18 @@ class lessc_parser { return false; } - // consume a list of property values delimited by ; and wrapped in () - protected function argumentValues(&$args, $delim = ',') { - $s = $this->seek(); - if (!$this->literal('(')) return false; - - $values = array(); - while (true) { - if ($this->expressionList($value)) $values[] = $value; - if (!$this->literal($delim)) break; - else { - if ($value == null) $values[] = null; - $value = null; - } - } - - if (!$this->literal(')')) { - $this->seek($s); - return false; - } - - $args = $values; - return true; - } - // consume an argument definition list surrounded by () // each argument is a variable name with optional value // or at the end a ... or a variable named followed by ... - protected function argumentDef(&$args, &$isVararg, $delim = ',') { + // arguments are separated by , unless a ; is in the list, then ; is the + // delimiter. + protected function argumentDef(&$args, &$isVararg) { $s = $this->seek(); if (!$this->literal('(')) return false; $values = array(); + $delim = ","; + $method = "expressionList"; $isVararg = false; while (true) { @@ -2734,28 +2935,77 @@ class lessc_parser { break; } - if ($this->variable($vname)) { - $arg = array("arg", $vname); - $ss = $this->seek(); - if ($this->assign() && $this->expressionList($value)) { - $arg[] = $value; - } else { - $this->seek($ss); - if ($this->literal("...")) { - $arg[0] = "rest"; - $isVararg = true; + if ($this->$method($value)) { + if ($value[0] == "variable") { + $arg = array("arg", $value[1]); + $ss = $this->seek(); + + if ($this->assign() && $this->$method($rhs)) { + $arg[] = $rhs; + } else { + $this->seek($ss); + if ($this->literal("...")) { + $arg[0] = "rest"; + $isVararg = true; + } } + + $values[] = $arg; + if ($isVararg) break; + continue; + } else { + $values[] = array("lit", $value); } - $values[] = $arg; - if ($isVararg) break; - continue; } - if ($this->value($literal)) { - $values[] = array("lit", $literal); - } - if (!$this->literal($delim)) break; + if (!$this->literal($delim)) { + if ($delim == "," && $this->literal(";")) { + // found new delim, convert existing args + $delim = ";"; + $method = "propertyValue"; + + // transform arg list + if (isset($values[1])) { // 2 items + $newList = array(); + foreach ($values as $i => $arg) { + switch($arg[0]) { + case "arg": + if ($i) { + $this->throwError("Cannot mix ; and , as delimiter types"); + } + $newList[] = $arg[2]; + break; + case "lit": + $newList[] = $arg[1]; + break; + case "rest": + $this->throwError("Unexpected rest before semicolon"); + } + } + + $newList = array("list", ", ", $newList); + + switch ($values[0][0]) { + case "arg": + $newArg = array("arg", $values[0][1], $newList); + break; + case "lit": + $newArg = array("lit", $newList); + break; + } + + } elseif ($values) { // 1 item + $newArg = $values[0]; + } + + if ($newArg) { + $values = array($newArg); + } + } else { + break; + } + } } if (!$this->literal(')')) { @@ -2797,70 +3047,136 @@ class lessc_parser { } // a bracketed value (contained within in a tag definition) - protected function tagBracket(&$value) { + protected function tagBracket(&$parts, &$hasExpression) { // speed shortcut if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") { return false; } $s = $this->seek(); - if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) { - $value = '['.$c.']'; - // whitespace? - if ($this->whitespace()) $value .= " "; - // escape parent selector, (yuck) - $value = str_replace($this->lessc->parentSelector, "$&$", $value); - return true; - } + $hasInterpolation = false; - $this->seek($s); - return false; - } + if ($this->literal("[", false)) { + $attrParts = array("["); + // keyword, string, operator + while (true) { + if ($this->literal("]", false)) { + $this->count--; + break; // get out early + } - protected function tagExpression(&$value) { - $s = $this->seek(); - if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { - $value = array('exp', $exp); - return true; + if ($this->match('\s+', $m)) { + $attrParts[] = " "; + continue; + } + if ($this->string($str)) { + // escape parent selector, (yuck) + foreach ($str[2] as &$chunk) { + $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk); + } + + $attrParts[] = $str; + $hasInterpolation = true; + continue; + } + + if ($this->keyword($word)) { + $attrParts[] = $word; + continue; + } + + if ($this->interpolation($inter, false)) { + $attrParts[] = $inter; + $hasInterpolation = true; + continue; + } + + // operator, handles attr namespace too + if ($this->match('[|-~\$\*\^=]+', $m)) { + $attrParts[] = $m[0]; + continue; + } + + break; + } + + if ($this->literal("]", false)) { + $attrParts[] = "]"; + foreach ($attrParts as $part) { + $parts[] = $part; + } + $hasExpression = $hasExpression || $hasInterpolation; + return true; + } + $this->seek($s); } $this->seek($s); return false; } - // a single tag + // a space separated list of selectors protected function tag(&$tag, $simple = false) { if ($simple) - $chars = '^,:;{}\][>\(\) "\''; + $chars = '^@,:;{}\][>\(\) "\''; else - $chars = '^,;{}["\''; + $chars = '^@,;{}["\''; - if (!$simple && $this->tagExpression($tag)) { - return true; - } + $s = $this->seek(); - $tag = ''; - while ($this->tagBracket($first)) $tag .= $first; + $hasExpression = false; + $parts = array(); + while ($this->tagBracket($parts, $hasExpression)); + + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; while (true) { if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) { - $tag .= $m[1]; + $parts[] = $m[1]; if ($simple) break; - while ($this->tagBracket($brack)) $tag .= $brack; + while ($this->tagBracket($parts, $hasExpression)); continue; - } elseif ($this->unit($unit)) { // for keyframes - $tag .= $unit[1] . $unit[2]; + } + + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") { + if ($this->interpolation($interp)) { + $hasExpression = true; + $interp[2] = true; // don't unescape + $parts[] = $interp; + continue; + } + + if ($this->literal("@")) { + $parts[] = "@"; + continue; + } + } + + if ($this->unit($unit)) { // for keyframes + $parts[] = $unit[1]; + $parts[] = $unit[2]; continue; } + break; } + $this->eatWhiteDefault = $oldWhite; + if (!$parts) { + $this->seek($s); + return false; + } - $tag = trim($tag); - if ($tag == '') return false; + if ($hasExpression) { + $tag = array("exp", array("string", "", $parts)); + } else { + $tag = trim(implode($parts)); + } + $this->whitespace(); return true; } @@ -2948,7 +3264,7 @@ class lessc_parser { protected function end() { if ($this->literal(';')) { return true; - } elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') { + } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') { // if there is end of file or a closing block next then we don't need a ; return true; } @@ -3212,7 +3528,7 @@ class lessc_parser { break; case '"': case "'": - if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count)) + if (preg_match('/'.$min[0].'.*?(?