// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.collide.client.code.autocomplete.css; import com.google.collide.client.code.autocomplete.AutocompleteProposal; import com.google.collide.json.client.Jso; import com.google.collide.json.client.JsoArray; import com.google.common.annotations.VisibleForTesting; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.regexp.shared.RegExp; import elemental.js.util.JsMapFromStringToBoolean; /** * Based on the complete property list for CSS2, this partial parser parses * already existing values and proposes autocompletions for the slot where the * cursor currently is. * */ public class CssPartialParser { /** * Singleton instance. */ private static CssPartialParser instance; // TODO: What about "-.5"? private static final String NUMBER_PATTERN = "(-|\\+)?\\d+(\\.\\d+)?"; private static final String PERCENTAGE_PATTERN = "(\\d|[1-9]\\d|100)%"; private static final String BYTE_PATTERN = "(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])"; private static final String LENGTH_PATTERN = NUMBER_PATTERN + "(em|ex|in|cm|mm|pt|pc|px)"; private static final RegExp REGEXP_INTEGER = compileRegExp("(-|\\+)?\\d+"); private static final RegExp REGEXP_PERCENTAGE = compileRegExp(PERCENTAGE_PATTERN); private static final RegExp REGEXP_3_HEX = compileRegExp("#[0-9a-fA-F]{3}"); private static final RegExp REGEXP_6_HEX = compileRegExp("#[0-9a-fA-F]{6}"); private static final RegExp REGEXP_RGB_BYTES = compileRegExp( "rgb\\(" + BYTE_PATTERN + "\\,\\s*" + BYTE_PATTERN + "\\,\\s*" + BYTE_PATTERN + "\\)"); private static final RegExp REGEXP_RGB_PERCENTS = compileRegExp("rgb\\(" + PERCENTAGE_PATTERN + "\\,\\s*" + PERCENTAGE_PATTERN + "\\,\\s*" + PERCENTAGE_PATTERN + "\\)"); private static final RegExp REGEXP_ANGLE = compileRegExp(NUMBER_PATTERN + "(deg|grad|rad)"); private static final RegExp REGEXP_NUMBER = compileRegExp(NUMBER_PATTERN); private static final RegExp REGEXP_FREQUENCY = compileRegExp(NUMBER_PATTERN + "k?Hz"); private static final RegExp REGEXP_LENGTH = compileRegExp(LENGTH_PATTERN); private static final RegExp REGEXP_RECT = compileRegExp("rect\\(\\s*(" + LENGTH_PATTERN + "|auto)\\s*\\,\\s*(" + LENGTH_PATTERN + "|auto)\\s*\\,\\s*(" + LENGTH_PATTERN + "|auto)\\s*\\,\\s*(" + LENGTH_PATTERN + "|auto)\\s*\\)"); private static final String ANGLE = "<angle>"; private static final String NUMBER = "<number>"; private static final String INTEGER = "<integer>"; private static final String URI = "<uri>"; private static final String PERCENTAGE = "<percentage>"; private static final String STRING = "<string>"; private static final String COUNTER = "<counter>"; private static final String IDENTIFIER = "<identifier>"; private static final String FREQUENCY = "<frequency>"; private static final String COLOR = "<color>"; private static final String LENGTH = "<length>"; private static final String SHAPE = "<shape>"; private final JavaScriptObject allProperties; private final JavaScriptObject specialValueProposals; private final JsMapFromStringToBoolean repeatingProperties = JsMapFromStringToBoolean.create(); public static CssPartialParser getInstance() { if (instance == null) { instance = new CssPartialParser(); } return instance; } /** * Creates {@link RegExp} instance by given pattern that matches the * whole string. */ private static RegExp compileRegExp(String pattern) { return RegExp.compile("^" + pattern + "$"); } private CssPartialParser() { allProperties = setupAllProperties(); specialValueProposals = setupSpecialValueProposals(); setupRepeatingProperties(repeatingProperties); } private boolean checkIfAngle(String maybeSpecialValue) { return REGEXP_ANGLE.test(maybeSpecialValue); } private boolean checkIfNumber(String maybeSpecialValue) { return REGEXP_NUMBER.test(maybeSpecialValue); } private boolean checkIfInteger(String maybeSpecialValue) { return REGEXP_INTEGER.test(maybeSpecialValue); } private boolean checkIfUri(String maybeSpecialValue) { // TODO: This is an oversimplification. return (maybeSpecialValue.startsWith("http://") || maybeSpecialValue.startsWith("https://")); } private boolean checkIfPercentage(String maybeSpecialValue) { return REGEXP_PERCENTAGE.test(maybeSpecialValue); } private boolean checkIfString(String maybeSpecialValue) { return (((maybeSpecialValue.charAt(0) == '"') && (maybeSpecialValue.charAt(maybeSpecialValue.length() - 1) == '"')) || ((maybeSpecialValue.charAt(0) == '\'') && (maybeSpecialValue.charAt(maybeSpecialValue.length() - 1) == '\''))); } private boolean checkIfCounter(String maybeSpecialValue) { // TODO: This is a simplification. return maybeSpecialValue.startsWith("counter("); } private boolean checkIfIdentifier(String maybeSpecialValue) { // TODO: Implement this. return false; } private boolean checkIfFrequency(String maybeSpecialValue) { return REGEXP_FREQUENCY.test(maybeSpecialValue); } private boolean checkIfColor(String maybeSpecialValue) { return maybeSpecialValue.equals("aqua") || maybeSpecialValue.equals("black") || maybeSpecialValue.equals("blue") || maybeSpecialValue.equals("fuchsia") || maybeSpecialValue.equals("gray") || maybeSpecialValue.equals("green") || maybeSpecialValue.equals("lime") || maybeSpecialValue.equals("maroon") || maybeSpecialValue.equals("navy") || maybeSpecialValue.equals("olive") || maybeSpecialValue.equals("orange") || maybeSpecialValue.equals("purple") || maybeSpecialValue.equals("red") || maybeSpecialValue.equals("silver") || maybeSpecialValue.equals("teal") || maybeSpecialValue.equals("white") || maybeSpecialValue.equals("yellow") || REGEXP_3_HEX.test(maybeSpecialValue) || REGEXP_6_HEX.test(maybeSpecialValue) || REGEXP_RGB_BYTES.test(maybeSpecialValue) || REGEXP_RGB_PERCENTS.test(maybeSpecialValue); } private boolean checkIfLength(String maybeSpecialValue) { return REGEXP_LENGTH.test(maybeSpecialValue); } private boolean checkIfShape(String maybeSpecialValue) { // Note: the syntax for shape is rect(<top>, <right>, <bottom>, <left>) return REGEXP_RECT.test(maybeSpecialValue); } /** * Checks whether the passed-in string matches the format of the special value * type in question. If it does, it returns a list of proposals that should be * shown to the user for this query. * * @param maybeSpecialValue * @param specialValueType a special value type, e.g., <integer>. This method * can be called with a specialValueType value that is not one of those * special values, in which case the method returns an empty array. * @return an array of strings corresponding to the proposals that should be * shown for the special value type */ @VisibleForTesting public JsArrayString checkIfSpecialValueAndGetSpecialValueProposals( String maybeSpecialValue, String specialValueType) { JsArrayString specialValues = getSpecialValues(maybeSpecialValue); for (int i = 0; i < specialValues.length(); i++) { if (specialValues.get(i).equals(specialValueType)) { return specialValueProposals.<Jso>cast().getJsObjectField(specialValueType).cast(); } } return JavaScriptObject.createArray().cast(); } public JsoArray<AutocompleteProposal> getAutocompletions( String property, JsArrayString valuesBefore, String incomplete, JsArrayString valuesAfter) { incomplete = incomplete.toLowerCase(); boolean isRepeatingProperty = repeatingProperties.hasKey(property); JsArrayString valuesAndSpecialValuesAfter = getValuesAndSpecialValues(valuesAfter); JsArrayString valuesAndSpecialValuesBefore = getValuesAndSpecialValues(valuesBefore); JsoArray<AutocompleteProposal> proposals = JsoArray.create(); JsArray<JavaScriptObject> valuesForAllSlots = getPropertyValues(property); if (valuesForAllSlots == null) { return proposals; } int numSlots = valuesForAllSlots.length(); if (numSlots == 0) { return proposals; } int slot = valuesBefore.length(); // slots use 0-based counting if (slot >= numSlots) { // before giving up, see if the last entry is +, in which case we just // adjust the slot number JavaScriptObject lastSlotValues = valuesForAllSlots.get(numSlots - 1); JsArrayString keySet = getKeySet(lastSlotValues); if ((keySet.length() == 1) && (keySet.get(0).equals("+"))) { slot = numSlots - 1; } else { return proposals; } } JavaScriptObject valuesForSlotInQuestion = valuesForAllSlots.get(slot); JsArrayString keySet = getKeySet(valuesForSlotInQuestion); if ((keySet.length() == 1) && (keySet.get(0).equals("+"))) { valuesForSlotInQuestion = valuesForAllSlots.get(slot - 1); keySet = getKeySet(valuesForSlotInQuestion); } if (valuesForSlotInQuestion != null) { if (keySet.length() == 0) { return proposals; } for (int keyCt = 0; keyCt < keySet.length(); keyCt++) { String currentValue = keySet.get(keyCt); Boolean shouldBeIncluded = false; // TODO: Avoid using untyped native collections. JavaScriptObject triggers = valuesForSlotInQuestion.<Jso>cast().getJsObjectField(currentValue).cast(); JsArrayString keyTriggerSet = getKeySet(triggers); if (keyTriggerSet.length() == 0) { if (currentValue.charAt(0) == '<') { JsArrayString valueProposals = specialValueProposals.<Jso>cast().getJsObjectField(currentValue).cast(); if (valueProposals != null && valueProposals.length() != 0) { shouldBeIncluded = false; for (int i = 0; i < valueProposals.length(); i++) { if (valueProposals.get(i).startsWith(incomplete) && (isRepeatingProperty || !inExistingValues( currentValue, valuesAndSpecialValuesBefore, valuesAndSpecialValuesAfter))) { proposals.add(new AutocompleteProposal(valueProposals.get(i))); } } } } else { shouldBeIncluded = true; } } else { for (int keyTriggerCt = 0; keyTriggerCt < keyTriggerSet.length(); keyTriggerCt++) { String triggerValue = keyTriggerSet.get(keyTriggerCt); int triggerSlot = triggers.<Jso>cast().getIntField(triggerValue); if (triggerValue.charAt(0) == '<') { JsArrayString specialValueProposalsAfterCheck = checkIfSpecialValueAndGetSpecialValueProposals( valuesBefore.get(triggerSlot), triggerValue); if (specialValueProposalsAfterCheck.length() != 0) { shouldBeIncluded = false; for (int i = 0; i < specialValueProposalsAfterCheck.length(); i++) { if (specialValueProposalsAfterCheck.get(i).startsWith(incomplete) && (isRepeatingProperty || !inExistingValues(triggerValue, valuesAndSpecialValuesBefore, valuesAndSpecialValuesAfter))) { proposals.add(new AutocompleteProposal(specialValueProposalsAfterCheck.get(i))); } } } } else if (valuesBefore.get(triggerSlot).compareTo(triggerValue) == 0) { shouldBeIncluded = true; } } } if (shouldBeIncluded) { if (currentValue.startsWith(incomplete) && (isRepeatingProperty || !inExistingValues( currentValue, valuesAndSpecialValuesBefore, valuesAndSpecialValuesAfter))) { proposals.add(new AutocompleteProposal(currentValue)); } } } } return proposals; } private boolean inExistingValues(String currentValue, JsArrayString valuesAndSpecialValuesAfter, JsArrayString valuesAndSpecialValuesBefore) { for (int i = 0; i < valuesAndSpecialValuesAfter.length(); i++) { if (valuesAndSpecialValuesAfter.get(i).equals(currentValue)) { return true; } } for (int i = 0; i < valuesAndSpecialValuesBefore.length(); i++) { if (valuesAndSpecialValuesBefore.get(i).equals(currentValue)) { return true; } } return false; } private JsArrayString getValuesAndSpecialValues(JsArrayString existingValues) { JsArrayString valuesAndSpecialValuesAfter = JavaScriptObject.createArray().cast(); for (int i = 0; i < existingValues.length(); i++) { String value = existingValues.get(i); JsArrayString specialValues = getSpecialValues(value); if (specialValues.length() == 0) { valuesAndSpecialValuesAfter.push(value); } for (int j = 0; j < specialValues.length(); j++) { valuesAndSpecialValuesAfter.push(specialValues.get(j)); } } return valuesAndSpecialValuesAfter; } private JsArrayString getSpecialValues(String value) { JsArrayString specialValues = JavaScriptObject.createArray().cast(); if (value.isEmpty()) { return specialValues; } if (checkIfAngle(value)) { specialValues.push(ANGLE); } if (checkIfInteger(value)) { specialValues.push(INTEGER); } if (checkIfNumber(value)) { specialValues.push(NUMBER); } if (checkIfUri(value)) { specialValues.push(URI); } if (checkIfPercentage(value)) { specialValues.push(PERCENTAGE); } if (checkIfString(value)) { specialValues.push(STRING); } if (checkIfCounter(value)) { specialValues.push(COUNTER); } if (checkIfIdentifier(value)) { specialValues.push(IDENTIFIER); } if (checkIfFrequency(value)) { specialValues.push(FREQUENCY); } if (checkIfColor(value)) { specialValues.push(COLOR); } if (checkIfLength(value)) { specialValues.push(LENGTH); } if (checkIfShape(value)) { specialValues.push(SHAPE); } return specialValues; } @VisibleForTesting public JsArray<JavaScriptObject> getPropertyValues(String property) { return allProperties.<Jso>cast().getJsObjectField(property).cast(); } private native JsArrayString getKeySet(JavaScriptObject jso) /*-{ var accumulator = []; for ( var propertyName in jso) { accumulator.push(propertyName); } return accumulator; }-*/; private native JavaScriptObject setupAllProperties() /*-{ return { 'azimuth' : [ { 'left-side' : {}, 'far-left' : {}, 'center-left' : {}, 'center' : {}, 'center-right' : {}, 'right' : {}, 'far-right' : {}, 'right-side' : {}, '<angle>' : {}, 'leftwards' : {}, 'rightwards' : {}, 'inherit' : {} }, { 'behind' : { 'left-side' : 0, 'far-left' : 0, 'center-left' : 0, 'center' : 0, 'center-right' : 0, 'right' : 0, 'far-right' : 0, 'right-side' : 0, '<angle>' : 0 } } ], 'background' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} }, { '<uri>' : {}, 'none' : {}, 'inherit' : {} }, { 'repeat' : {}, 'repeat-x' : {}, 'repeat-y' : {}, 'no-repeat' : {}, 'inherit' : {} }, { 'scroll' : {}, 'fixed' : {}, 'inherit' : {} }, { '<percentage>' : {}, '<length>' : {}, 'left' : {}, 'center' : {}, 'right' : {}, 'inherit' : {} }, { '<percentage>' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, '<length>' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, 'top' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, 'center' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, 'bottom' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 } } ], 'background-attachment' : [ { 'scroll' : {}, 'fixed' : {}, 'inherit' : {} } ], 'background-color' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} } ], 'background-image' : [ { '<uri>' : {}, 'none' : {}, 'inherit' : {} } ], 'background-position' : [ { '<percentage>' : {}, '<length>' : {}, 'left' : {}, 'center' : {}, 'right' : {}, 'inherit' : {} }, { '<percentage>' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, '<length>' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, 'top' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, 'center' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 }, 'bottom' : { '<percentage>' : 0, '<length>' : 0, 'left' : 0, 'center' : 0, 'right' : 0 } } ], 'background-repeat' : [ { 'repeat' : {}, 'repeat-x' : {}, 'repeat-y' : {}, 'no-repeat' : {}, 'inherit' : {} } ], 'border-collapse' : [ { 'collapse' : {}, 'separate' : {}, 'inherit' : {} } ], 'border-color' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} }, { '<color>' : { '<color>' : 0, 'transparent' : 0 }, 'transparent' : { '<color>' : 0, 'transparent' : 0 } }, { '<color>' : { '<color>' : 1, 'transparent' : 1 }, 'transparent' : { '<color>' : 1, 'transparent' : 1 } }, { '<color>' : { '<color>' : 2, 'transparent' : 2 }, 'transparent' : { '<color>' : 2, 'transparent' : 2 } } ], 'border-spacing' : [ { '<length>' : {}, 'inherit' : {} }, { '<length>' : { '<length>' : 0 } } ], 'border-style' : [ { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} }, { 'none' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'hidden' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'dotted' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'dashed' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'solid' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'double' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'groove' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'ridge' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'inset' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 }, 'outset' : { 'none' : 0, 'hidden' : 0, 'dotted' : 0, 'dashed' : 0, 'solid' : 0, 'double' : 0, 'groove' : 0, 'ridge' : 0, 'inset' : 0, 'outset' : 0 } }, { 'none' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'hidden' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'dotted' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'dashed' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'solid' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'double' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'groove' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'ridge' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'inset' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'outset' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 } }, { 'none' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'hidden' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'dotted' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'dashed' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'solid' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'double' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'groove' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'ridge' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'inset' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 }, 'outset' : { 'none' : 2, 'hidden' : 2, 'dotted' : 2, 'dashed' : 2, 'solid' : 2, 'double' : 2, 'groove' : 2, 'ridge' : 2, 'inset' : 2, 'outset' : 2 } } ], 'border-top' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} }, { 'none' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'hidden' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dotted' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dashed' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'solid' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'double' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'groove' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'ridge' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'outset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inherit' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 } }, { '<color>' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'transparent' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'inherit' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 } } ], 'border-bottom' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} }, { 'none' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'hidden' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dotted' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dashed' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'solid' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'double' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'groove' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'ridge' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'outset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inherit' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 } }, { '<color>' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'transparent' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'inherit' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 } } ], 'border-left' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} }, { 'none' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'hidden' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dotted' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dashed' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'solid' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'double' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'groove' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'ridge' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'outset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inherit' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 } }, { '<color>' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'transparent' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'inherit' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 } } ], 'border-right' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} }, { 'none' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'hidden' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dotted' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'dashed' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'solid' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'double' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'groove' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'ridge' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'outset' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'inherit' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 } }, { '<color>' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'transparent' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 }, 'inherit' : { 'none' : 1, 'hidden' : 1, 'dotted' : 1, 'dashed' : 1, 'solid' : 1, 'double' : 1, 'groove' : 1, 'ridge' : 1, 'inset' : 1, 'outset' : 1 } } ], 'border-top-color' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} } ], 'border-bottom-color' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} } ], 'border-left-color' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} } ], 'border-right-color' : [ { '<color>' : {}, 'transparent' : {}, 'inherit' : {} } ], 'border-top-style' : [ { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} } ], 'border-bottom-style' : [ { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} } ], 'border-left-style' : [ { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} } ], 'border-right-style' : [ { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} } ], 'border-top-width' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} } ], 'border-bottom-width' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} } ], 'border-left-width' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} } ], 'border-right-width' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} } ], 'border-width' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} }, { 'thin' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'medium' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, 'thick' : { 'thin' : 0, 'medium' : 0, 'thick' : 0, '<length>' : 0 }, '<length>' : { 'thin' : 1, 'medium' : 1, 'thick' : 1, '<length>' : 1 } } ], 'border' : [ { '<length>' : {}, 'inherit' : {}, 'transparent' : {}, 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'thin' : {}, 'medium' : {}, 'thick' : {}, '<color>' : {} }, { '+' : {} } ], 'bottom' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'caption-side' : [ { 'top' : {}, 'bottom' : {}, 'inherit' : {} } ], 'clear' : [ { 'none' : {}, 'left' : {}, 'right' : {}, 'both' : {}, 'inherit' : {} } ], 'clip' : [ { '<shape>' : {}, 'auto' : {}, 'inherit' : {} } ], 'color' : [ { '<color>' : {}, 'inherit' : {} } ], 'content' : [ { 'normal' : {}, 'none' : {}, '<string>' : {}, '<uri>' : {}, '<counter>' : {}, 'attr(<identifier>)' : {}, 'open-quote' : {}, 'close-quote' : {}, 'no-open-quote' : {}, 'no-close-quote' : {}, 'inherit' : {} }, { '<string>' : {}, '<uri>' : {}, '<counter>' : {}, 'attr(<identifier>)' : {}, 'open-quote' : {}, 'close-quote' : {}, 'no-open-quote' : {}, 'no-close-quote' : {} }, { '+' : {} } ], 'counter-increment' : [ { '<identifier>' : {}, '<identifierWithInteger>' : {}, 'none' : {}, 'inherit' : {} }, { '<identifier>' : { '<identifier>' : 0, '<identifierWithInteger>' : 0 }, '<identifierWithInteger>' : { '<identifier>' : 0, '<identifierWithInteger>' : 0 } }, { '+' : {} } ], 'counter-reset' : [ { '<identifier>' : {}, '<identifierWithInteger>' : {}, 'none' : {}, 'inherit' : {} }, { '<identifier>' : { '<identifier>' : 0, '<identifierWithInteger>' : 0 }, '<identifierWithInteger>' : { '<identifier>' : 0, '<identifierWithInteger>' : 0 } }, { '+' : {} } ], 'cue-after' : [ { '<uri>' : {}, 'none' : {}, 'inherit' : {} } ], 'cue-before' : [ { '<uri>' : {}, 'none' : {}, 'inherit' : {} } ], 'cue' : [ { 'cue-before' : {}, 'inherit' : {} }, { 'cue-after' : { 'cue-before' : 0 } } ], 'cursor' : [ { '<uri> ,' : {}, 'auto' : {}, 'crosshair' : {}, 'default' : {}, 'pointer' : {}, 'move' : {}, 'e-resize' : {}, 'ne-resize' : {}, 'nw-resize' : {}, 'n-resize' : {}, 'se-resize' : {}, 'sw-resize' : {}, 's-resize' : {}, 'w-resize' : {}, 'text' : {}, 'wait' : {}, 'help' : {}, 'progress' : {}, 'inherit' : {} }, { '<uri> ,' : { '<uri>' : 0 } }, { '+' : {} } ], 'direction' : [ { 'ltr' : {}, 'rtl' : {}, 'inherit' : {} } ], 'display' : [ { 'inline' : {}, 'block' : {}, 'list-item' : {}, 'run-in' : {}, 'inline-block' : {}, 'table' : {}, 'inline-table' : {}, 'table-row-group' : {}, 'table-header-group' : {}, 'table-footer-group' : {}, 'table-row' : {}, 'table-column-group' : {}, 'table-column' : {}, 'table-cell' : {}, 'table-caption' : {}, 'none' : {}, 'inherit' : {} } ], 'elevation' : [ { '<angle>' : {}, 'below' : {}, 'level' : {}, 'above' : {}, 'higher' : {}, 'lower' : {}, 'inherit' : {} } ], 'empty-cells' : [ { 'show' : {}, 'hide' : {}, 'inherit' : {} } ], 'float' : [ { 'left' : {}, 'right' : {}, 'none' : {}, 'inherit' : {} } ], 'font-family' : [ { '<family-name>' : {}, 'serif' : {}, 'sans-serif' : {}, 'cursive' : {}, 'fantasy' : {}, 'monospace' : {}, inherit : {} }, { ', <family-name>' : { '<family-name>' : 0, 'serif' : 0, 'sans-serif' : 0, 'cursive' : 0, 'fantasy' : 0, 'monospace' : 0 }, ', serif' : { '<family-name>' : 0, 'serif' : 0, 'sans-serif' : 0, 'cursive' : 0, 'fantasy' : 0, 'monospace' : 0 }, ', sans-serif' : { '<family-name>' : 0, 'serif' : 0, 'sans-serif' : 0, 'cursive' : 0, 'fantasy' : 0, 'monospace' : 0 }, ', cursive' : { '<family-name>' : 0, 'serif' : 0, 'sans-serif' : 0, 'cursive' : 0, 'fantasy' : 0, 'monospace' : 0 }, ', fantasy' : { '<family-name>' : 0, 'serif' : 0, 'sans-serif' : 0, 'cursive' : 0, 'fantasy' : 0, 'monospace' : 0 }, ', monospace' : { '<family-name>' : 0, 'serif' : 0, 'sans-serif' : 0, 'cursive' : 0, 'fantasy' : 0, 'monospace' : 0 } }, { '+' : {} } ], 'font-size' : [ { '<absolute-size>' : {}, '<relative-size>' : {}, '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'font-style' : [ { 'normal' : {}, 'italic' : {}, 'oblique' : {}, 'inherit' : {} } ], 'font-variant' : [ { 'normal' : {}, 'small-caps' : {}, 'inherit' : {} } ], 'font-weight' : [ { 'normal' : {}, 'bold' : {}, 'bolder' : {}, 'lighter' : {}, '100' : {}, '200' : {}, '300' : {}, '400' : {}, '500' : {}, '600' : {}, '700' : {}, '800' : {}, '900' : {}, 'inherit' : {} } ], 'font' : [ { 'serif' : {}, 'sans-serif' : {}, 'cursive' : {}, 'fantasy' : {}, 'monospace' : {}, '<family-name>' : {}, 'inherit' : {}, 'xx-small' : {}, 'x-small' : {}, 'small' : {}, 'medium' : {}, 'large' : {}, 'x-large' : {}, 'xx-large' : {}, '<length>' : {}, 'larger' : {}, 'smaller' : {}, 'italic' : {}, 'normal' : {}, 'oblique' : {}, 'small-caps' : {}, '100' : {}, '200' : {}, '300' : {}, '400' : {}, '500' : {}, '600' : {}, '700' : {}, '800' : {}, '900' : {}, 'bold' : {}, 'bolder' : {}, 'lighter' : {}, '<number>' : {}, '<percentage>' : {}, 'caption' : {}, 'icon' : {}, 'menu' : {}, 'message-box' : {}, 'small-caption' : {}, 'status-bar' : {} }, { '+' : {} } ], 'height' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'left' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'letter-spacing' : [ { 'normal' : {}, '<length>' : {}, 'inherit' : {} } ], 'line-height' : [ { 'normal' : {}, '<number>' : {}, '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'list-style-image' : [ { '<uri>' : {}, 'none' : {}, 'inherit' : {} } ], 'list-style-position' : [ { 'inside' : {}, 'outside' : {}, 'inherit' : {} } ], 'list-style-type' : [ { 'disc' : {}, 'circle' : {}, 'square' : {}, 'decimal' : {}, 'decimal-leading-zero' : {}, 'lower-roman' : {}, 'upper-roman' : {}, 'lower-greek' : {}, 'lower-latin' : {}, 'upper-latin' : {}, 'armenian' : {}, 'georgian' : {}, 'lower-alpha' : {}, 'upper-alpha' : {}, 'none' : {}, 'inherit' : {} } ], 'list-style' : [ { 'disc' : {}, 'circle' : {}, 'square' : {}, 'decimal' : {}, 'decimal-leading-zero' : {}, 'lower-roman' : {}, 'upper-roman' : {}, 'lower-greek' : {}, 'lower-latin' : {}, 'upper-latin' : {}, 'armenian' : {}, 'georgian' : {}, 'lower-alpha' : {}, 'upper-alpha' : {}, 'none' : {}, 'inherit' : {} }, { 'inside' : {}, 'outside' : {}, 'inherit' : {} }, { '<uri>' : {}, 'none' : {}, 'inherit' : {} } ], 'margin-top' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'margin-bottom' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'margin-left' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'margin-right' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'margin' : [ { '<length>' : {}, '<percentage>' : {}, 'none' : {}, 'inherit' : {} }, { '<length>' : {}, '<percentage>' : {}, 'none' : {}, 'inherit' : {} }, { '<length>' : {}, '<percentage>' : {}, 'none' : {}, 'inherit' : {} }, { '<length>' : {}, '<percentage>' : {}, 'none' : {}, 'inherit' : {} } ], 'max-height' : [ { '<length>' : {}, '<percentage>' : {}, 'none' : {}, 'inherit' : {} } ], 'max-width' : [ { '<length>' : {}, '<percentage>' : {}, 'none' : {}, 'inherit' : {} } ], 'min-height' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'min-width' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'orphans' : [ { '<integer>' : {}, 'inherit' : {} } ], 'outline-color' : [ { '<color>' : {}, 'invert' : {}, 'inherit' : {} } ], 'outline-style' : [ { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} } ], 'outline-width' : [ { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} } ], 'outline' : [ { '<color>' : {}, 'invert' : {}, 'inherit' : {} }, { 'none' : {}, 'hidden' : {}, 'dotted' : {}, 'dashed' : {}, 'solid' : {}, 'double' : {}, 'groove' : {}, 'ridge' : {}, 'inset' : {}, 'outset' : {}, 'inherit' : {} }, { 'thin' : {}, 'medium' : {}, 'thick' : {}, '<length>' : {}, 'inherit' : {} } ], 'overflow' : [ { 'visible' : {}, 'hidden' : {}, 'scroll' : {}, 'auto' : {}, 'inherit' : {} } ], 'padding-top' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'padding-bottom' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'padding-left' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'padding-right' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'padding' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} }, { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} }, { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} }, { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'page-break-after' : [ { 'auto' : {}, 'always' : {}, 'avoid' : {}, 'left' : {}, 'right' : {}, 'inherit' : {} } ], 'page-break-before' : [ { 'auto' : {}, 'always' : {}, 'avoid' : {}, 'left' : {}, 'right' : {}, 'inherit' : {} } ], 'page-break-inside' : [ { 'avoid' : {}, 'auto' : {}, 'inherit' : {} } ], 'pause-after' : [ { '<time>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'pause-before' : [ { '<time>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'pause' : [ { '<time>' : {}, '<percentage>' : {}, 'inherit' : {} }, { '<time>' : { '<time>' : 0, '<percentage>' : 0 }, '<percentage>' : { '<time>' : 0, '<percentage>' : 0 } } ], 'pitch-range' : [ { '<number>' : {}, 'inherit' : {} } ], 'pitch' : [ { '<frequency>' : {}, 'x-low' : {}, 'low' : {}, 'medium' : {}, 'high' : {}, 'x-high' : {}, 'inherit' : {} } ], 'play-during' : [ { '<uri>' : {}, 'auto' : {}, 'none' : {}, 'inherit' : {} }, { 'mix' : { '<uri>' : 0 }, 'repeat' : { '<uri>' : 0 } } ], 'position' : [ { 'static' : {}, 'relative' : {}, 'absolute' : {}, 'fixed' : {}, 'inherit' : {} } ], 'quotes' : [ { '<string>' : {}, 'none' : {}, 'inherit' : {} }, { '<string>' : { '<string>' : 0 } }, { '+' : {} } ], 'richness' : [ { '<number>' : {}, 'inherit' : {} } ], 'right' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'speak-header' : [ { 'once' : {}, 'always' : {}, 'inherit' : {} } ], 'speak-numeral' : [ { 'digits' : {}, 'continuous' : {}, 'inherit' : {} } ], 'speak-punctuation' : [ { 'code' : {}, 'none' : {}, 'inherit' : {} } ], 'speak' : [ { 'normal' : {}, 'none' : {}, 'spell-out' : {}, 'inherit' : {} } ], 'speech-rate' : [ { '<number>' : {}, 'x-slow' : {}, 'slow' : {}, 'medium' : {}, 'fast' : {}, 'x-fast' : {}, 'faster' : {}, 'slower' : {}, 'inherit' : {} } ], 'stress' : [ { '<number>' : {}, 'inherit' : {} } ], 'table-layout' : [ { 'auto' : {}, 'fixed' : {}, 'inherit' : {} } ], 'text-align' : [ { 'left' : {}, 'right' : {}, 'center' : {}, 'justify' : {}, 'inherit' : {} } ], 'text-decoration' : [ { 'none' : {}, 'underline' : {}, 'inherit' : {} }, { 'overline' : { 'underline' : 0 } }, { 'line-through' : { 'overline' : 1 } }, { 'blink' : { 'line-through' : 2 } } ], 'text-indent' : [ { '<length>' : {}, '<percentage>' : {}, 'inherit' : {} } ], 'text-transform' : [ { 'capitalize' : {}, 'uppercase' : {}, 'lowercase' : {}, 'none' : {}, 'inherit' : {} } ], 'top' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'unicode-bidi' : [ { 'normal' : {}, 'embed' : {}, 'bidi-override' : {}, 'inherit' : {} } ], 'vertical-align' : [ { 'baseline' : {}, 'sub' : {}, 'super' : {}, 'top' : {}, 'text-top' : {}, 'middle' : {}, 'bottom' : {}, 'text-bottom' : {}, '<percentage>' : {}, '<length>' : {}, 'inherit' : {} } ], 'visibility' : [ { 'visible' : {}, 'hidden' : {}, 'collapse' : {}, 'inherit' : {} } ], 'voice-family' : [ { 'comedian' : {}, 'trinoids' : {}, 'carlos' : {}, 'lani' : {}, 'male' : {}, 'female' : {}, 'child' : {}, 'comedian,' : {}, 'trinoids,' : {}, 'carlos,' : {}, 'lani,' : {}, 'male,' : {}, 'female,' : {}, 'child,' : {}, 'inherit' : {} }, { 'comedian' : { 'comedian,' : {}, 'trinoids,' : {}, 'carlos,' : {}, 'lani,' : {}, 'male,' : {}, 'female,' : {}, 'child,' : {} }, 'trinoids' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'carlos' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'lani' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'male' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'female' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'child' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'comedian,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'trinoids,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'carlos,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'lani,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'male,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'female,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 }, 'child,' : { 'comedian,' : 0, 'trinoids,' : 0, 'carlos,' : 0, 'lani,' : 0, 'male,' : 0, 'female,' : 0, 'child,' : 0 } }, { '+' : {} } ], 'volume' : [ { '<number>' : {}, '<percentage>' : {}, 'silent' : {}, 'x-soft' : {}, 'soft' : {}, 'medium' : {}, 'loud' : {}, 'x-loud' : {}, 'inherit' : {} } ], 'white-space' : [ { 'normal' : {}, 'pre' : {}, 'nowrap' : {}, 'pre-wrap' : {}, 'pre-line' : {}, 'inherit' : {} } ], 'widows' : [ { '<integer>' : {}, 'inherit' : {} } ], 'width' : [ { '<length>' : {}, '<percentage>' : {}, 'auto' : {}, 'inherit' : {} } ], 'word-spacing' : [ { 'normal' : {}, '<length>' : {}, 'inherit' : {} } ], 'z-index' : [ { 'auto' : {}, '<integer>' : {}, 'inherit' : {} } ] }; }-*/; private native JavaScriptObject setupSpecialValueProposals() /*-{ return { '<angle>' : [ '<number>deg', '<number>grad', '<number>rad' ], '<family-name>' : [ '<family-name>' ], '<number>' : [ '<number>' ], '<integer>' : [ '<integer>' ], '<color>' : [ 'black', 'blue', 'fuchsia', 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple', 'red', 'silver', 'teal', 'white', 'yellow', 'rgb(<number>, <number>, <number>)', 'rgb(<percentage>, <percentage>, <percentage>', '#rgb', '#rrggbb' ], '<uri>' : [ '<uri>' ], '<length>' : [ '<number>em', '<number>ex', '<number>in', '<number>cm', '<number>mm', '<number>pt', '<number>pc', '<number>px' ], '<percentage>' : [ '<number>%' ], '<relative-size>' : [ '<relative-size>' ], '<absolute-size>' : [ '<absolute-size>' ], '<shape>' : [ '<shape>' ], '<string>' : [ '<string>' ], '<counter>' : [ '<counter>' ], '<identifier>' : [ '<identifer>' ], '<frequency>' : [ '<frequency>' ] }; }-*/; // TODO: Make this a bit smarter by allowing only for repetition up // to a certain number. private void setupRepeatingProperties(JsMapFromStringToBoolean repeatingProperties) { repeatingProperties.put("border-color", true); repeatingProperties.put("border-style", true); repeatingProperties.put("border-width", true); repeatingProperties.put("margin", true); repeatingProperties.put("quotes", true); repeatingProperties.put("padding", true); repeatingProperties.put("pause", true); } }