Fixes #902 - case insensitive attribute matchers.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Tue, 14 Mar 2017 09:35:54 +0000 (10:35 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Wed, 15 Mar 2017 10:07:40 +0000 (11:07 +0100)
Why:

* CSS 4 spec defines case insensitive matchers which were treated
  incorrectly by attribute tidying code.

History.md
lib/optimizer/level-1/tidy-rules.js
test/optimizer/level-1/optimize-test.js

index 454c826..583fbb4 100644 (file)
@@ -8,6 +8,7 @@
 [4.0.9 / 2017-xx-xx](https://github.com/jakubpawlowicz/clean-css/compare/v4.0.8...4.0)
 ==================
 
+* Fixed issue [#902](https://github.com/jakubpawlowicz/clean-css/issues/902) - case insensitive attribute matchers.
 * Fixed issue [#907](https://github.com/jakubpawlowicz/clean-css/issues/907) - space after closing brace in `@supports`.
 * Fixed issue [#910](https://github.com/jakubpawlowicz/clean-css/issues/910) - too aggressive precision optimizations.
 
index 0bd7073..5aba2c7 100644 (file)
@@ -2,7 +2,13 @@ var Spaces = require('../../options/format').Spaces;
 var Marker = require('../../tokenizer/marker');
 var formatPosition = require('../../utils/format-position');
 
+var CASE_ATTRIBUTE_PATTERN = /[\s"'][iI]\s*\]/;
+var CASE_RESTORE_PATTERN = /([\d\w])([iI])\]/g;
+var DOUBLE_QUOTE_CASE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"([iI])/g;
+var DOUBLE_QUOTE_PATTERN = /="([a-zA-Z][a-zA-Z\d\-_]+)"(\s|\])/g;
 var HTML_COMMENT_PATTERN = /^(?:(?:<!--|-->)\s*)+/;
+var SINGLE_QUOTE_CASE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'([iI])/g;
+var SINGLE_QUOTE_PATTERN = /='([a-zA-Z][a-zA-Z\d\-_]+)'(\s|\])/g;
 var RELATION_PATTERN = /[>\+~]/;
 var WHITESPACE_PATTERN = /\s/;
 
@@ -54,6 +60,7 @@ function removeWhitespace(value, format) {
   var roundBracketLevel = 0;
   var wasRelation = false;
   var wasWhitespace = false;
+  var withCaseAttribute = CASE_ATTRIBUTE_PATTERN.test(value);
   var spaceAroundRelation = format && format.spaces[Spaces.AroundSelectorRelation];
   var i, l;
 
@@ -128,13 +135,21 @@ function removeWhitespace(value, format) {
     wasWhitespace = isWhitespace;
   }
 
-  return stripped.join('');
+  return stripped
+    .join('')
+    .replace(withCaseAttribute ? CASE_RESTORE_PATTERN : null, '$1 $2]');
 }
 
 function removeQuotes(value) {
+  if (value.indexOf('\'') == -1 && value.indexOf('"') == -1) {
+    return value;
+  }
+
   return value
-    .replace(/='([a-zA-Z][a-zA-Z\d\-_]+)'/g, '=$1')
-    .replace(/="([a-zA-Z][a-zA-Z\d\-_]+)"/g, '=$1');
+    .replace(SINGLE_QUOTE_CASE_PATTERN, '=$1 $2')
+    .replace(SINGLE_QUOTE_PATTERN, '=$1$2')
+    .replace(DOUBLE_QUOTE_CASE_PATTERN, '=$1 $2')
+    .replace(DOUBLE_QUOTE_PATTERN, '=$1$2');
 }
 
 function tidyRules(rules, removeUnsupported, adjacentSpace, format, warnings) {
index bedebdb..6f66d93 100644 (file)
@@ -105,6 +105,18 @@ vows.describe('level 1 optimizations')
         '.b[data-json=\'"aaaa":"bbbb"\']{color:red}',
         '.b[data-json=\'"aaaa":"bbbb"\']{color:red}'
       ],
+      'single word case insensitive attribute with quotes': [
+        '.block[data-value="test" i]{color:red}',
+        '.block[data-value=test i]{color:red}'
+      ],
+      'multiword case insensitive attribute with quotes': [
+        '.block[data-value="test me" i]{color:red}',
+        '.block[data-value="test me"i]{color:red}'
+      ],
+      'single word case insensitive attribute without quotes': [
+        '.block[data-value=test i]{color:red}',
+        '.block[data-value=test i]{color:red}'
+      ],
       'no rule scope': [
         '{overflow:hidden}',
         ''