123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- /*
- * Copyright 2014, The gwtquery team.
- *
- * 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.gwt.query.client.plugins.effects;
-
- import static com.google.gwt.query.client.plugins.Effects.prefix;
- import static com.google.gwt.query.client.plugins.Effects.vendorPropNames;
- import static com.google.gwt.query.client.plugins.Effects.vendorProperty;
-
- import com.google.gwt.dom.client.Document;
- import com.google.gwt.dom.client.Element;
- import com.google.gwt.dom.client.Style;
- import com.google.gwt.query.client.GQuery;
- import com.google.gwt.query.client.js.JsUtils;
- import com.google.gwt.regexp.shared.MatchResult;
- import com.google.gwt.regexp.shared.RegExp;
-
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map.Entry;
-
- /**
- * A dictionary class with all the properties of an element transform
- * which is able to return the correct syntax for setting the CSS transform
- * property.
- */
- public class Transform {
-
- private static final String TRANSFORM = "_t_";
-
- // Used to check supported properties in the browser
- protected static final Style divStyle = Document.get().createDivElement().getStyle();
-
- // Compute browser specific constants, public so as they are usable in plugins
-
- static {
- for (String s: new String[]{"transition", "transitionDelay", "transform", "transformOrigin"}) {
- vendorPropNames.put(s, getVendorPropertyName(s));
- }
- // x,y,z are aliases
- for (String s: new String[]{"x", "y", "z"}) {
- vendorPropNames.put(s, "translate" + s.toUpperCase());
- }
- }
-
- public static final String TRANSFORM_1 = vendorProperty("transform");
- public static final String TRANSFORM_ORIGIN = vendorProperty("transformOrigin");
-
- // Non final for testing purposes.
- public static boolean has3d = supportsTransform3d();
-
- // Regular expressions based on http://www.w3schools.com/cssref/css3_pr_transform.asp
- protected static final RegExp transformRegex = RegExp.compile("^(matrix(3d)?|(translate|scale|rotate)([XYZ]|3d)?|skew([XY])?|perspective|x|y|z)$");
- private static final RegExp transform3dRegex = RegExp.compile("^(rotate[XY]|\\w+(Z|3d)|perspective)$");
- private static final RegExp transformParseRegex = RegExp.compile("(\\w+)\\((.*?)\\)", "g");
- private static final RegExp anglePropRegex = RegExp.compile("(rotate[XYZ]?|skew[XY]?)");
- private static final RegExp translatePropRegex = RegExp.compile("translate[XYZ]");
-
- private HashMap<String, List<String>> map = new HashMap<>();
-
- // Some browsers like HTMLUnit only support 2d transformations
- private static boolean supportsTransform3d() {
- if (TRANSFORM_1 == null) {
- return false;
- }
- String rotate = "rotateY(1deg)";
- divStyle.setProperty(TRANSFORM_1, rotate);
- rotate = divStyle.getProperty(TRANSFORM_1);
- return rotate != null && !rotate.isEmpty();
- }
-
- /**
- * Compute the correct CSS property name for a specific browser vendor.
- */
- public static String getVendorPropertyName(String prop) {
- // we prefer vendor specific names by default
- String vendorProp = JsUtils.camelize("-" + prefix + "-" + prop);
- if (JsUtils.hasProperty(divStyle, vendorProp)) {
- return vendorProp;
- }
- if (JsUtils.hasProperty(divStyle, prop)) {
- return prop;
- }
- String camelProp = JsUtils.camelize(prop);
- if (JsUtils.hasProperty(divStyle, camelProp)) {
- return camelProp;
- }
- return null;
- }
-
-
- /**
- * Return the Transform dictionary object of a element.
- */
- public static Transform getInstance(Element e) {
- return getInstance(e, null);
- }
-
- /**
- * Return true if the propName is a valid value of the css3 transform property.
- */
- public static boolean isTransform(String propName) {
- return transformRegex.test(propName);
- }
-
- /**
- * Return the Transform dictionary object of an element, but reseting
- * historical values and setting them to the initial value passed.
- */
- public static Transform getInstance(Element e, String initial) {
- Transform t = GQuery.data(e, TRANSFORM);
- if (t == null || initial != null) {
- if (initial == null) {
- initial = GQuery.getSelectorEngine().getDocumentStyleImpl().curCSS(e, TRANSFORM_1, false);
- }
- t = new Transform(initial);
- GQuery.data(e, TRANSFORM, t);
- }
- return t;
- }
-
- /**
- * Create a new Transform dictionary setting initial values based on the
- * string passed.
- */
- public Transform(String s) {
- parse(s);
- }
-
- /**
- * Return the value of a transform property.
- */
- public String get(String prop) {
- return listToStr(map.get(prop), ",");
- }
-
- private String listToStr(List<String> l, String sep) {
- String v = "";
- if (l != null) {
- for (String s : l) {
- v += (v.isEmpty() ? "" : sep) + s;
- }
- }
- return v;
- }
-
- /**
- * Parse a transform value as string and fills the dictionary map.
- */
- private void parse(String s) {
- if (s != null) {
- for (MatchResult r = transformParseRegex.exec(s); r != null; r = transformParseRegex.exec(s)) {
- setFromString(vendorProperty(r.getGroup(1)), r.getGroup(2));
- }
- }
- }
-
- /**
- * Set a transform value or multi-value.
- */
- public void set(String prop, String ...val) {
- setter(prop, val);
- }
-
- /**
- * Set a transform multi-value giving either a set of strings or
- * just an string of values separated by comma.
- */
- public void setFromString(String prop, String ...val) {
- if (val.length == 1) {
- String[] vals = val[0].split("[\\s,]+");
- set(prop, vals);
- } else {
- set(prop, val);
- }
- }
-
- private void setter(String prop, String ...val) {
- if (anglePropRegex.test(prop)) {
- map.put(prop, unit(val[0], "deg"));
- } else if ("scale".equals(prop)) {
- String x = val.length < 1 ? "1" : val[0];
- String y = val.length < 2 ? x : val[1];
- map.put(prop, Arrays.asList(x, y));
- } else if ("perspective".equals(prop)) {
- map.put(prop, unit(val[0], "px"));
- } else if (translatePropRegex.test(prop)) {
- map.put(prop, unit(val[0], "px"));
- } else if ("translate".equals(prop)) {
- if (val[0] != null) {
- map.put("translateX", unit(val[0], "px"));
- }
- if (val.length > 1 && val[1] != null) {
- map.put("translateY", unit(val[1], "px"));
- }
- if (has3d && val.length > 2 && val[2] != null) {
- map.put("translateZ", unit(val[2], "px"));
- }
- } else {
- map.put(prop, Arrays.asList(val));
- }
- }
-
- /**
- * Converts the dictionary to a transition css string value but
- * excluding 3d properties if the browser only supports 2d.
- */
- public String toString() {
- // purposely using string addition, since my last tests demonstrate
- // that string addition performs better than string builders in gwt-prod.
- String ret = "";
- for (Entry<String, List<String>> e: map.entrySet()) {
- if (has3d || !transform3dRegex.test(e.getKey())) {
- String v = listToStr(e.getValue(), ",");
- ret += (ret.isEmpty() ? "" : " ") + e.getKey() + "(" + v + ")";
- }
- }
- return ret;
- }
-
- private List<String> unit(String val, String unit) {
- return Arrays.asList(val + (val.endsWith(unit) ? "" : unit));
- }
- }
|