can-override.js 5.37 KB
// Functions that decide what value can override what.
// The main purpose is to disallow removing CSS fallbacks.
// A separate implementation is needed for every different kind of CSS property.
// -----
// The generic idea is that properties that have wider browser support are 'more understandable'
// than others and that 'less understandable' values can't override more understandable ones.

// Use when two tokens of the same property can always be merged
function always() {
  return true;
}

function alwaysButIntoFunction(property1, property2, validator) {
  var value1 = property1.value[0][0];
  var value2 = property2.value[0][0];

  var validFunction1 = validator.isValidFunction(value1);
  var validFunction2 = validator.isValidFunction(value2);

  if (validFunction1 && validFunction2) {
    return validator.areSameFunction(value1, value2);
  } else if (!validFunction1 && validFunction2) {
    return false;
  } else {
    return true;
  }
}

function backgroundImage(property1, property2, validator) {
  // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
  // Understandability: (none | url | inherit) > (same function) > (same value)

  // (none | url)
  var image1 = property1.value[0][0];
  var image2 = property2.value[0][0];

  if (image2 == 'none' || image2 == 'inherit' || validator.isValidUrl(image2))
    return true;
  if (image1 == 'none' || image1 == 'inherit' || validator.isValidUrl(image1))
    return false;

  // Functions with the same name can override each other; same values can override each other
  return sameFunctionOrValue(property1, property2, validator);
}

function border(property1, property2, validator) {
  return color(property1.components[2], property2.components[2], validator);
}

// Use for color properties (color, background-color, border-color, etc.)
function color(property1, property2, validator) {
  // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
  // Understandability: (hex | named) > (rgba | hsla) > (same function name) > anything else
  // NOTE: at this point rgb and hsl are replaced by hex values by clean-css

  var color1 = property1.value[0][0];
  var color2 = property2.value[0][0];

  if (!validator.colorOpacity && (validator.isValidRgbaColor(color1) || validator.isValidHslaColor(color1)))
    return false;
  if (!validator.colorOpacity && (validator.isValidRgbaColor(color2) || validator.isValidHslaColor(color2)))
    return false;

  // (hex | named)
  if (validator.isValidNamedColor(color2) || validator.isValidHexColor(color2))
    return true;
  if (validator.isValidNamedColor(color1) || validator.isValidHexColor(color1))
    return false;

  // (rgba|hsla)
  if (validator.isValidRgbaColor(color2) || validator.isValidHslaColor(color2))
    return true;
  if (validator.isValidRgbaColor(color1) || validator.isValidHslaColor(color1))
    return false;

  // Functions with the same name can override each other; same values can override each other
  return sameFunctionOrValue(property1, property2, validator);
}

function twoOptionalFunctions(property1, property2, validator) {
  var value1 = property1.value[0][0];
  var value2 = property2.value[0][0];

  return !(validator.isValidFunction(value1) ^ validator.isValidFunction(value2));
}

function sameValue(property1, property2) {
  var value1 = property1.value[0][0];
  var value2 = property2.value[0][0];

  return value1 === value2;
}

function sameFunctionOrValue(property1, property2, validator) {
  var value1 = property1.value[0][0];
  var value2 = property2.value[0][0];

  // Functions with the same name can override each other
  if (validator.areSameFunction(value1, value2))
    return true;

  return value1 === value2;
}

// Use for properties containing CSS units (margin-top, padding-left, etc.)
function unit(property1, property2, validator) {
  // The idea here is that 'more understandable' values override 'less understandable' values, but not vice versa
  // Understandability: (unit without functions) > (same functions | standard functions) > anything else
  // NOTE: there is no point in having different vendor-specific functions override each other or standard functions,
  //       or having standard functions override vendor-specific functions, but standard functions can override each other
  // NOTE: vendor-specific property values are not taken into consideration here at the moment
  var value1 = property1.value[0][0];
  var value2 = property2.value[0][0];

  if (validator.isValidAndCompatibleUnitWithoutFunction(value1) && !validator.isValidAndCompatibleUnitWithoutFunction(value2))
    return false;

  if (validator.isValidUnitWithoutFunction(value2))
    return true;
  if (validator.isValidUnitWithoutFunction(value1))
    return false;

  // Standard non-vendor-prefixed functions can override each other
  if (validator.isValidFunctionWithoutVendorPrefix(value2) && validator.isValidFunctionWithoutVendorPrefix(value1)) {
    return true;
  }

  // Functions with the same name can override each other; same values can override each other
  return sameFunctionOrValue(property1, property2, validator);
}

module.exports = {
  always: always,
  alwaysButIntoFunction: alwaysButIntoFunction,
  backgroundImage: backgroundImage,
  border: border,
  color: color,
  sameValue: sameValue,
  sameFunctionOrValue: sameFunctionOrValue,
  twoOptionalFunctions: twoOptionalFunctions,
  unit: unit
};