Skip to content

Commit

Permalink
Implemented MediaFeatureName enum and MediaQueryExpression class in p…
Browse files Browse the repository at this point in the history
…reparation for media query support. Code is based on that from webkit.
  • Loading branch information
danfickle committed Feb 22, 2014
1 parent 4a90bb3 commit 8fbcb6f
Show file tree
Hide file tree
Showing 3 changed files with 313 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.xhtmlrenderer.css.mediaquery;

import java.util.HashMap;
import java.util.Map;

public enum MediaFeatureName
{
COLOR("color"),
COLOR_INDEX("color-index"),
GRID("grid"),
MONOCHROME("monochrome"),
HEIGHT("height"),
HOVER("hover"),
WIDTH("width"),
ORIENTATION("orientation"),
ASPECT_RATIO("aspect-ratio"),
DEVICE_ASPECT_RATIO("device-aspect-ratio"),
DEVICE_HEIGHT("device-height"),
DEVICE_WIDTH("device-width"),
MAX_COLOR("max-color"),
MAX_COLOR_INDEX("max-color-index"),
MAX_ASPECT_RATIO("max-aspect-ratio"),
MAX_DEVICE_ASPECT_RATIO("max-device-aspect-ratio"),
MAX_DEVICE_HEIGHT("max-device-height"),
MAX_DEVICE_WIDTH("max-device-width"),
MAX_HEIGHT("max-height"),
MAX_MONOCHROME("max-monochrome"),
MAX_WIDTH("max-width"),
MAX_RESOLUTION("max-resolution"),
MIN_COLOR("min-color"),
MIN_COLOR_INDEX("min-color-index"),
MIN_ASPECT_RATIO("min-aspect-ratio"),
MIN_DEVICE_ASPECT_RATIO("min-device-aspect-ratio"),
MIN_DEVICE_HEIGHT("min-device-height"),
MIN_DEVICE_WIDTH("min-device-width"),
MIN_HEIGHT("min-height"),
MIN_MONOCHROME("min-monochrome"),
MIN_WIDTH("min-width"),
MIN_RESOLUTION("min-resolution"),
POINTER("pointer"),
RESOLUTION("resolution");

private final String cssName;

private MediaFeatureName(String name)
{
cssName = name;
}

@Override
public String toString()
{
return cssName;
}

private static final Map<String, MediaFeatureName> map = new HashMap<>(values().length);

static
{
for (MediaFeatureName nm : values())
{
map.put(nm.cssName, nm);
}
}

public static MediaFeatureName fsValueOf(String mediaFeatureStr)
{
return map.get(mediaFeatureStr);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package org.xhtmlrenderer.css.mediaquery;

import java.util.Arrays;
import java.util.List;

import org.w3c.dom.css.CSSPrimitiveValue;
import org.xhtmlrenderer.css.constants.IdentValue;
import org.xhtmlrenderer.css.parser.PropertyValue;
import org.xhtmlrenderer.css.parser.Token;
import org.xhtmlrenderer.css.parser.property.BuilderUtil;

public class MediaQueryExpression
{
private final MediaFeatureName _mediaFeature;
private final PropertyValue _cssValue;
private final boolean _isValid;

public boolean isValid()
{
return _isValid;
}

public MediaFeatureName mediaFeature()
{
return _mediaFeature;
}

public PropertyValue value()
{
return _cssValue;
}

public boolean isViewportDependent()
{
return _mediaFeature == MediaFeatureName.WIDTH
|| _mediaFeature == MediaFeatureName.HEIGHT
|| _mediaFeature == MediaFeatureName.MIN_WIDTH
|| _mediaFeature == MediaFeatureName.MIN_HEIGHT
|| _mediaFeature == MediaFeatureName.MAX_WIDTH
|| _mediaFeature == MediaFeatureName.MAX_HEIGHT
|| _mediaFeature == MediaFeatureName.ORIENTATION
|| _mediaFeature == MediaFeatureName.ASPECT_RATIO
|| _mediaFeature == MediaFeatureName.MIN_ASPECT_RATIO
|| _mediaFeature == MediaFeatureName.MAX_ASPECT_RATIO;
}

private static boolean featureWithCSSValueID(MediaFeatureName mediaFeature, PropertyValue value)
{
IdentValue result = IdentValue.fsValueOf(value.getStringValue());

if (result == null)
return false;

return mediaFeature == MediaFeatureName.ORIENTATION
|| mediaFeature == MediaFeatureName.POINTER;
}

private static boolean featureWithValidPositiveLenghtOrNumber(MediaFeatureName mediaFeature, PropertyValue value)
{
if (!BuilderUtil.isLength(value) && value.getPrimitiveType() != CSSPrimitiveValue.CSS_PERCENTAGE)
return false;

if (value.getFloatValue() < 0)
return false;

return mediaFeature == MediaFeatureName.HEIGHT
|| mediaFeature == MediaFeatureName.MAX_HEIGHT
|| mediaFeature == MediaFeatureName.MIN_HEIGHT
|| mediaFeature == MediaFeatureName.WIDTH
|| mediaFeature == MediaFeatureName.MAX_WIDTH
|| mediaFeature == MediaFeatureName.MIN_WIDTH
|| mediaFeature == MediaFeatureName.DEVICE_HEIGHT
|| mediaFeature == MediaFeatureName.MAX_DEVICE_HEIGHT
|| mediaFeature == MediaFeatureName.MIN_DEVICE_HEIGHT
|| mediaFeature == MediaFeatureName.DEVICE_WIDTH
|| mediaFeature == MediaFeatureName.MAX_DEVICE_WIDTH
|| mediaFeature == MediaFeatureName.MIN_DEVICE_WIDTH;
}

private static boolean featureWithValidDensity(MediaFeatureName mediaFeature, PropertyValue value)
{
// TODO: Support these types...
//if ((value.getPrimitiveType() != CSSPrimitiveValue.CSS_DPPX && value.getPrimitiveType() != CSSPrimitiveValue.CSS_DPI && value.getPrimitiveType() != CSSPrimitiveValue.CSS_DPCM) || value.getFloatValue() <= 0)
// return false;

return mediaFeature == MediaFeatureName.RESOLUTION
|| mediaFeature == MediaFeatureName.MAX_RESOLUTION
|| mediaFeature == MediaFeatureName.MIN_RESOLUTION;
}


private static boolean featureWithPositiveInteger(MediaFeatureName mediaFeature, PropertyValue value)
{
if ((value.getFloatValue() % 1f) != 0f || value.getFloatValue() < 0)
return false;

return mediaFeature == MediaFeatureName.COLOR
|| mediaFeature == MediaFeatureName.MAX_COLOR
|| mediaFeature == MediaFeatureName.MIN_COLOR
|| mediaFeature == MediaFeatureName.COLOR_INDEX
|| mediaFeature == MediaFeatureName.MAX_COLOR_INDEX
|| mediaFeature == MediaFeatureName.MIN_COLOR_INDEX
|| mediaFeature == MediaFeatureName.MIN_MONOCHROME
|| mediaFeature == MediaFeatureName.MAX_MONOCHROME;
}

private static boolean featureWithPositiveNumber(MediaFeatureName mediaFeature, PropertyValue value)
{
if (value.getPrimitiveType() != CSSPrimitiveValue.CSS_NUMBER || value.getFloatValue() < 0)
return false;

// No non-webkit specific media expressions fit this cirteria.
return false;
}

private static boolean featureWithZeroOrOne(MediaFeatureName mediaFeature, PropertyValue value)
{
if ((value.getFloatValue() % 1f) != 0f || !(value.getFloatValue() == 1 || value.getFloatValue() == 0))
return false;

return mediaFeature == MediaFeatureName.GRID
|| mediaFeature == MediaFeatureName.HOVER;
}

private static boolean featureWithAspectRatio(MediaFeatureName mediaFeature)
{
return mediaFeature == MediaFeatureName.ASPECT_RATIO
|| mediaFeature == MediaFeatureName.DEVICE_ASPECT_RATIO
|| mediaFeature == MediaFeatureName.MIN_ASPECT_RATIO
|| mediaFeature == MediaFeatureName.MAX_ASPECT_RATIO
|| mediaFeature == MediaFeatureName.MIN_DEVICE_ASPECT_RATIO
|| mediaFeature == MediaFeatureName.MAX_DEVICE_ASPECT_RATIO;
}

private static boolean featureWithoutValue(MediaFeatureName mediaFeature)
{
// Media features that are prefixed by min/max cannot be used without a value.
return mediaFeature == MediaFeatureName.MONOCHROME
|| mediaFeature == MediaFeatureName.COLOR
|| mediaFeature == MediaFeatureName.COLOR_INDEX
|| mediaFeature == MediaFeatureName.GRID
|| mediaFeature == MediaFeatureName.HEIGHT
|| mediaFeature == MediaFeatureName.WIDTH
|| mediaFeature == MediaFeatureName.DEVICE_HEIGHT
|| mediaFeature == MediaFeatureName.DEVICE_WIDTH
|| mediaFeature == MediaFeatureName.ORIENTATION
|| mediaFeature == MediaFeatureName.ASPECT_RATIO
|| mediaFeature == MediaFeatureName.DEVICE_ASPECT_RATIO
|| mediaFeature == MediaFeatureName.HOVER
|| mediaFeature == MediaFeatureName.POINTER
|| mediaFeature == MediaFeatureName.RESOLUTION;
}

@Override
public String toString()
{
StringBuilder result = new StringBuilder();
result.append('(');
result.append(_mediaFeature.toString());
if (_cssValue != null) {
result.append(": ");
result.append(_cssValue.getCssText());
}
result.append(')');

return result.toString();
}

public MediaQueryExpression(String mediaFeatureStr, List<PropertyValue> valueList)
{
_mediaFeature = MediaFeatureName.fsValueOf(mediaFeatureStr);

// Initialize media query expression that must have 1 or more values.
if (valueList == null &&
featureWithoutValue(_mediaFeature))
{
_isValid = true;
_cssValue = null;
return;
}

if (valueList.size() == 1)
{
_cssValue = valueList.get(0);

if (featureWithCSSValueID(_mediaFeature, _cssValue) ||
featureWithValidDensity(_mediaFeature, _cssValue) ||
featureWithValidPositiveLenghtOrNumber(_mediaFeature, _cssValue) ||
featureWithPositiveInteger(_mediaFeature, _cssValue) ||
featureWithPositiveNumber(_mediaFeature, _cssValue) ||
featureWithZeroOrOne(_mediaFeature, _cssValue))
{
_isValid = true;
return;
}
}
else if (valueList.size() == 2 && featureWithAspectRatio(_mediaFeature))
{
PropertyValue top = valueList.get(0);
PropertyValue bottom = valueList.get(1);

List<PropertyValue> values = Arrays.asList(top, bottom);

_cssValue = new PropertyValue(values);

// The aspect-ratio must be <integer> (whitespace)? / (whitespace)? <integer>.
if (top.getFloatValue() % 1f == 0f &&
bottom.getFloatValue() %1f == 0f &&
bottom.getOperator() == Token.TK_VIRGULE)
{
_isValid = true;
return;
}
}
else
{
_cssValue = null;
}

_isValid = false;
}
}
Loading

0 comments on commit 8fbcb6f

Please sign in to comment.