Implemented stylistic changes as asked
authorTimur Kristóf <venemo@msn.com>
Tue, 4 Mar 2014 09:36:47 +0000 (10:36 +0100)
committerTimur Kristóf <venemo@msn.com>
Tue, 4 Mar 2014 09:36:47 +0000 (10:36 +0100)
lib/properties/optimizer.js
lib/properties/override-compactor.js
lib/properties/processable.js
lib/properties/shorthand-compactor.js
lib/properties/validator.js

index 4fa83c2..b2e30b5 100644 (file)
@@ -1,3 +1,8 @@
+
+var processableInfo = require('./processable');
+var overrideCompactor = require('./override-compactor');
+var shorthandCompactor = require('./shorthand-compactor');
+
 module.exports = function Optimizer(compatibility) {
   var overridable = {
     'animation-delay': ['animation'],
@@ -233,18 +238,17 @@ module.exports = function Optimizer(compatibility) {
     return flat.join(';');
   };
 
-  var p = require('./processable');
-  var overrideCompactor = require('./override-compactor');
-  var shorthandCompactor = require('./shorthand-compactor');
-
   var compact = function (input) {
-    var tokens = p.Token.tokenize(input);
+    var processable = processableInfo.processable;
+    var Token = processableInfo.Token;
+
+    var tokens = Token.tokenize(input);
 
-    tokens = overrideCompactor.compactOverrides(tokens, p.processable);
-    tokens = shorthandCompactor.compactShorthands(tokens, false, p.processable, p.Token);
-    tokens = shorthandCompactor.compactShorthands(tokens, true, p.processable, p.Token);
+    tokens = overrideCompactor.compactOverrides(tokens, processable);
+    tokens = shorthandCompactor.compactShorthands(tokens, false, processable, Token);
+    tokens = shorthandCompactor.compactShorthands(tokens, true, processable, Token);
 
-    return p.Token.detokenize(tokens);
+    return Token.detokenize(tokens);
   };
 
   return {
index b51f7b2..418cad2 100644 (file)
@@ -2,7 +2,10 @@
 // Compacts the given tokens according to their ability to override each other.
 
 module.exports = (function () {
-  var sameValue = function (val1, val2) { return val1 === val2; };
+  // Default override function: only allow overrides when the two values are the same
+  var sameValue = function (val1, val2) {
+    return val1 === val2;
+  };
 
   var compactOverrides = function (tokens, processable) {
     var result, can, token, t, i, ii, oldResult, matchingComponent;
@@ -17,17 +20,18 @@ module.exports = (function () {
     };
 
     // Go from the end and always take what the current token can't override as the new result set
+    // NOTE: can't cache result.length here because it will change with every iteration
     for (result = tokens, i = 0; (ii = result.length - 1 - i) >= 0; i++) {
       token = result[ii];
-      //console.log(i, ii, token.prop);
       can = (processable[token.prop] && processable[token.prop].canOverride) || sameValue;
       oldResult = result;
       result = [];
 
       // Special flag which indicates that the current token should be removed
       var removeSelf = false;
+      var oldResultLength = oldResult.length;
 
-      for (var iii = 0; iii < oldResult.length; iii++) {
+      for (var iii = 0; iii < oldResultLength; iii++) {
         t = oldResult[iii];
 
         // A token can't override itself (checked by reference, not by value)
@@ -59,8 +63,7 @@ module.exports = (function () {
             // The shorthand can't override the component
             result.push(t);
           }
-        }
-        else if (t.isShorthand && !token.isShorthand && token.isComponentOf(t)) {
+        } else if (t.isShorthand && !token.isShorthand && token.isComponentOf(t)) {
           // token (a component) is trying to override a component of t (a shorthand)
 
           // Find the matching component in the shorthand
@@ -74,8 +77,7 @@ module.exports = (function () {
               matchingComponent.value = token.value;
               // We use the special flag to get rid of the component
               removeSelf = true;
-            }
-            else {
+            } else {
               // The overriding component is important; sadly we can't get rid of it,
               // but we can still mark the matching component in the shorthand as irrelevant
               matchingComponent.isIrrelevant = true;
@@ -83,8 +85,7 @@ module.exports = (function () {
             t.isDirty = true;
           }
           result.push(t);
-        }
-        else if (token.isShorthand && t.isShorthand && token.prop === t.prop) {
+        } else if (token.isShorthand && t.isShorthand && token.prop === t.prop) {
           // token is a shorthand and is trying to override another instance of the same shorthand
 
           // Can only override other shorthand when each of its components can override each of the other's components
@@ -95,8 +96,7 @@ module.exports = (function () {
               break;
             }
           }
-        }
-        else if (t.prop !== token.prop || !can(t.value, token.value)) {
+        } else if (t.prop !== token.prop || !can(t.value, token.value)) {
           // in every other case, use the override mechanism
           result.push(t);
         }
index 8c9a7ac..988bf38 100644 (file)
@@ -1,8 +1,10 @@
 
 // Contains the interpretation of CSS properties, as used by the property optimizer
 
+
 module.exports = (function () {
 
+  var tokenModule = require('./token');
   var validator = require('./validator');
 
   // Functions that decide what value can override what.
@@ -138,22 +140,21 @@ module.exports = (function () {
     var result = [];
     var curr = '';
     var parenthesisLevel = 0;
+    var valLength = val.length;
 
-    for (var i = 0; i < val.length; i++) {
+    for (var i = 0; i < valLength; i++) {
       var c = val[i];
       curr += c;
 
       if (c === '(') {
         parenthesisLevel++;
-      }
-      else if (c === ')') {
+      } else if (c === ')') {
         parenthesisLevel--;
         if (parenthesisLevel === 0) {
           result.push(curr.trim());
           curr = '';
         }
-      }
-      else if (c === ' ' && parenthesisLevel === 0) {
+      } else if (c === ' ' && parenthesisLevel === 0) {
         result.push(curr.trim());
         curr = '';
       }
@@ -181,9 +182,8 @@ module.exports = (function () {
 
     // Break the background up into parts
     var parts = token.value.split(' ');
-    if (parts.length === 0) {
+    if (parts.length === 0)
       return result;
-    }
 
     // The trick here is that we start going through the parts from the end, then stop after background repeat,
     // then start from the from the beginning until we find a valid color value. What remains will be treated as background-image.
@@ -204,8 +204,7 @@ module.exports = (function () {
       position.value = pos;
       currentIndex -= 2;
       current = parts[currentIndex];
-    }
-    else if (currentIndex >= 0 && validator.isValidBackgroundPosition(current)) {
+    } else if (currentIndex >= 0 && validator.isValidBackgroundPosition(current)) {
       // Found position (containing just one part)
       position.value = current;
       currentIndex--;
@@ -318,33 +317,26 @@ module.exports = (function () {
     fourUnits: function (prop, tokens, isImportant) {
       // See about irrelevant tokens
       // NOTE: This will enable some crazy optimalizations for us.
-      if (tokens[0].isIrrelevant) {
+      if (tokens[0].isIrrelevant)
         tokens[0].value = tokens[2].value;
-      }
-      if (tokens[2].isIrrelevant) {
+      if (tokens[2].isIrrelevant)
         tokens[2].value = tokens[0].value;
-      }
-      if (tokens[1].isIrrelevant) {
+      if (tokens[1].isIrrelevant)
         tokens[1].value = tokens[3].value;
-      }
-      if (tokens[3].isIrrelevant) {
+      if (tokens[3].isIrrelevant)
         tokens[3].value = tokens[1].value;
-      }
+
       if (tokens[0].isIrrelevant && tokens[2].isIrrelevant) {
-        if (tokens[1].value === tokens[3].value) {
+        if (tokens[1].value === tokens[3].value)
           tokens[0].value = tokens[2].value = tokens[1].value;
-        }
-        else {
+        else
           tokens[0].value = tokens[2].value = '0';
-        }
       }
       if (tokens[1].isIrrelevant && tokens[3].isIrrelevant) {
-        if (tokens[0].value === tokens[2].value) {
+        if (tokens[0].value === tokens[2].value)
           tokens[1].value = tokens[3].value = tokens[0].value;
-        }
-        else {
+        else
           tokens[1].value = tokens[3].value = '0';
-        }
       }
 
       var result = new Token(prop, tokens[0].value, isImportant);
@@ -443,19 +435,18 @@ module.exports = (function () {
             var r2s = new Token(tokens[i].prop, tokens[i].isImportant);
             r2s.isIrrelevant = true;
             result2Shorthandable.push(r2s);
-          }
-          else {
+          } else {
             nonInheritingTokens.push(tokens[i]);
             result2Shorthandable.push(tokens[i]);
           }
         }
 
-        // When all the tokens are 'inherit'
         if (nonInheritingTokens.length === 0) {
+          // When all the tokens are 'inherit'
           return new Token(prop, 'inherit', isImportant);
-        }
-        // When some (but not all) of the tokens are 'inherit'
-        else if (inheritingTokens.length > 0) {
+        } else if (inheritingTokens.length > 0) {
+          // When some (but not all) of the tokens are 'inherit'
+
           // Result 1. Shorthand just the inherit values and have it overridden with the non-inheriting ones
           var result1 = [new Token(prop, 'inherit', isImportant)].concat(nonInheritingTokens);
 
@@ -467,9 +458,8 @@ module.exports = (function () {
           var dl2 = Token.getDetokenizedLength(result2);
 
           return dl1 < dl2 ? result1 : result2;
-        }
-        // When none of tokens are 'inherit'
-        else {
+        } else {
+          // When none of tokens are 'inherit'
           return innerFunc(prop, tokens, isImportant);
         }
       };
@@ -684,23 +674,25 @@ module.exports = (function () {
 
   // Set some stuff iteratively
   for (var proc in processable) {
-    if (processable.hasOwnProperty(proc)) {
-      var currDesc = processable[proc];
+    if (!processable.hasOwnProperty(proc))
+      continue;
 
-      if (currDesc.components instanceof Array && currDesc.components.length) {
-        currDesc.isShorthand = true;
+    var currDesc = processable[proc];
 
-        for (var cI = 0; cI < currDesc.components.length; cI++) {
-          if (!processable[currDesc.components[cI]]) {
-            throw new Error('"' + currDesc.components[cI] + '" is defined as a component of "' + proc + '" but isn\'t defined in processable.');
-          }
-          processable[currDesc.components[cI]].componentOf = proc;
-        }
+    if (!(currDesc.components instanceof Array) || currDesc.components.length === 0)
+      continue;
+
+    currDesc.isShorthand = true;
+
+    for (var cI = 0; cI < currDesc.components.length; cI++) {
+      if (!processable[currDesc.components[cI]]) {
+        throw new Error('"' + currDesc.components[cI] + '" is defined as a component of "' + proc + '" but isn\'t defined in processable.');
       }
+      processable[currDesc.components[cI]].componentOf = proc;
     }
   }
 
-  var Token = require('./token').createTokenPrototype(processable);
+  var Token = tokenModule.createTokenPrototype(processable);
 
   return {
     processable: processable,
index 177d789..12f7c98 100644 (file)
@@ -18,17 +18,19 @@ module.exports = (function () {
           var prop = processable[shprop].components[i];
           found[prop] = [];
 
-          if (componentsSoFar[shprop].found[prop]) {
-            for (var ii = 0; ii < componentsSoFar[shprop].found[prop].length; ii++) {
-              var comp = componentsSoFar[shprop].found[prop][ii];
-
-              if (!comp.isMarkedForDeletion) {
-                found[prop].push(comp);
-                if (comp.position && (!shorthandPosition || comp.position < shorthandPosition)) {
-                  shorthandPosition = comp.position;
-                }
-              }
-            }
+          if (!(componentsSoFar[shprop].found[prop]))
+            continue;
+
+          for (var ii = 0; ii < componentsSoFar[shprop].found[prop].length; ii++) {
+            var comp = componentsSoFar[shprop].found[prop][ii];
+
+            if (comp.isMarkedForDeletion)
+              continue;
+
+            found[prop].push(comp);
+
+            if (comp.position && (!shorthandPosition || comp.position < shorthandPosition))
+              shorthandPosition = comp.position;
           }
         }
       }
@@ -78,13 +80,11 @@ module.exports = (function () {
           if (foundRealComp.isReal !== false) {
             realComponents.push(foundRealComp);
           }
-        }
-        else if (componentsSoFar[prop].lastShorthand) {
+        } else if (componentsSoFar[prop].lastShorthand) {
           // It's defined in the previous shorthand
           var c = componentsSoFar[prop].lastShorthand.components[i].clone(isImportant);
           components.push(c);
-        }
-        else {
+        } else {
           // Couldn't find this component at all
           return false;
         }
@@ -192,15 +192,13 @@ module.exports = (function () {
         // TODO: when the old optimizer is removed, take care of this corner case:
         //   div{background-color:#111;background-image:url(aaa);background:linear-gradient(aaa);background-repeat:no-repeat;background-position:1px 2px;background-attachment:scroll}
         //   -> should not be shorthanded / minified at all because the result wouldn't be equivalent to the original in any browser
-      }
-      else if (processable[token.prop].componentOf) {
+      } else if (processable[token.prop].componentOf) {
         // Found a component of a shorthand
         if (token.isImportant === isImportant) {
           // Same importantness
           token.position = i;
           addComponentSoFar(token, i);
-        }
-        else if (!isImportant && token.isImportant) {
+        } else if (!isImportant && token.isImportant) {
           // Use importants for optimalization opportunities
           // https://github.com/GoalSmashers/clean-css/issues/184
           var importantTrickComp = new Token(token.prop, token.value, isImportant);
@@ -208,8 +206,7 @@ module.exports = (function () {
           importantTrickComp.isReal = false;
           addComponentSoFar(importantTrickComp);
         }
-      }
-      else {
+      } else {
         // This is not a shorthand and not a component, don't care about it
         continue;
       }
index 5ffce7f..6e12905 100644 (file)
@@ -3,11 +3,19 @@
 
 module.exports = (function () {
   // Regexes used for stuff
-  var cssUnitRegexStr =             '(\\-?\\.?\\d+\\.?\\d*(px|%|em|rem|in|cm|mm|ex|pt|pc|)|auto|inherit)';
+  var cssUnitRegexStr = '(\\-?\\.?\\d+\\.?\\d*(px|%|em|rem|in|cm|mm|ex|pt|pc|)|auto|inherit)';
   var cssFunctionNoVendorRegexStr = '[A-Z]+(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.)*\\)';
-  var cssFunctionVendorRegexStr =   '\\-(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.)*\\)';
-  var cssFunctionAnyRegexStr =      '(' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
-  var cssUnitAnyRegexStr =          '(' + cssUnitRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
+  var cssFunctionVendorRegexStr = '\\-(\\-|[A-Z]|[0-9])+\\(([A-Z]|[0-9]|\\ |\\,|\\#|\\+|\\-|\\%|\\.)*\\)';
+  var cssFunctionAnyRegexStr = '(' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
+  var cssUnitAnyRegexStr = '(' + cssUnitRegexStr + '|' + cssFunctionNoVendorRegexStr + '|' + cssFunctionVendorRegexStr + ')';
+
+  var backgroundRepeatKeywords = ['repeat', 'no-repeat', 'repeat-x', 'repeat-y', 'inherit'];
+  var backgroundAttachmentKeywords = ['inherit', 'scroll', 'fixed', 'local'];
+  var backgroundPositionKeywords = ['center', 'top', 'bottom', 'left', 'right'];
+  var listStyleTypeKeywords = ['armenian', 'circle', 'cjk-ideographic', 'decimal', 'decimal-leading-zero', 'disc', 'georgian', 'hebrew', 'hiragana', 'hiragana-iroha', 'inherit', 'katakana', 'katakana-iroha', 'lower-alpha', 'lower-greek', 'lower-latin', 'lower-roman', 'none', 'square', 'upper-alpha', 'upper-latin', 'upper-roman'];
+  var listStylePositionKeywords = ['inside', 'outside', 'inherit'];
+  var outlineStyleKeywords = ['inherit', 'hidden', 'none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
+  var outlineWidthKeywords = ['thin', 'thick', 'medium', 'inherit'];
 
   var validator = {
     isValidHexColor: function (s) {
@@ -48,37 +56,41 @@ module.exports = (function () {
       return new RegExp('^' + cssFunctionAnyRegexStr + '$', 'gi').test(s);
     },
     isValidBackgroundRepeat: function (s) {
-      return s === 'repeat' || s === 'no-repeat' || s === 'repeat-x' || s === 'repeat-y' || s === 'inherit';
+      return backgroundRepeatKeywords.indexOf(s) >= 0;
     },
     isValidBackgroundAttachment: function (s) {
-      return s === 'inherit' || s === 'scroll' || s === 'fixed' || s === 'local';
+      return backgroundAttachmentKeywords.indexOf(s) >= 0;
     },
     isValidBackgroundPositionPart: function (s) {
-      if (s === 'center' || s === 'top' || s === 'bottom' || s === 'left' || s === 'right')
+      if (backgroundPositionKeywords.indexOf(s) >= 0)
         return true;
-      // LIMITATION: currently we don't support functions in here because otherwise we'd confuse things like linear-gradient()
-      //             we need to figure out the complete list of functions that are allowed for units and then we can use isValidUnit here.
+
       return new RegExp('^' + cssUnitRegexStr + '$', 'gi').test(s);
     },
     isValidBackgroundPosition: function (s) {
       if (s === 'inherit')
         return true;
-      return s.split(' ').every(function(p) { return validator.isValidBackgroundPositionPart(p); });
+
+      return s.split(' ').filter(function (p) {
+        return p !== '';
+      }).every(function(p) {
+        return validator.isValidBackgroundPositionPart(p);
+      });
     },
     isValidListStyleType: function (s) {
-      return s === 'armenian' || s === 'circle' || s === 'cjk-ideographic' || s === 'decimal' || s === 'decimal-leading-zero' || s === 'disc' || s === 'georgian' || s === 'hebrew' || s === 'hiragana' || s === 'hiragana-iroha' || s === 'inherit' || s === 'katakana' || s === 'katakana-iroha' || s === 'lower-alpha' || s === 'lower-greek' || s === 'lower-latin' || s === 'lower-roman' || s === 'none' || s === 'square' || s === 'upper-alpha' || s === 'upper-latin' || s === 'upper-roman';
+      return listStyleTypeKeywords.indexOf(s) >= 0;
     },
     isValidListStylePosition: function (s) {
-      return s === 'inside' || s === 'outside' || s === 'inherit';
+      return listStylePositionKeywords.indexOf(s) >= 0;
     },
     isValidOutlineColor: function (s) {
       return s === 'invert' || validator.isValidColor(s) || validator.isValidVendorPrefixedValue(s);
     },
     isValidOutlineStyle: function (s) {
-      return s === 'inherit' || s === 'hidden' || s === 'none' || s === 'dotted' || s === 'dashed' || s === 'solid' || s === 'double' || s === 'groove' || s === 'ridge' || s === 'inset' || s === 'outset';
+      return outlineStyleKeywords.indexOf(s) >= 0;
     },
     isValidOutlineWidth: function (s) {
-      return validator.isValidUnit(s) || s === 'thin' || s === 'thick' || s === 'medium' || s === 'inherit';
+      return validator.isValidUnit(s) || outlineWidthKeywords.indexOf(s) >= 0;
     },
     isValidVendorPrefixedValue: function (s) {
       return /^-([A-Za-z0-9]|-)*$/gi.test(s);