/* * Copyright 2008-2009 Adam Tacy <adam.tacy AT gmail.com> * * 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. */ /* * Copyright 2011 Vancouver Ywebb Consulting Ltd * * 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 org.adamtacy.client.ui.effects.impl.css; import java.util.HashMap; import org.adamtacy.client.ui.effects.exception.ColorException; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; /** * Helper class providing access to Style manipulation functionality. * * @author Adam Tacy * @version 3.0 * */ public class StyleImplementation { static StyleSheetImplementationStandard styleSheetImpl = (StyleSheetImplementationStandard) GWT.create(StyleSheetImplementationStandard.class); /** * JavaScript require the use of Camel style for CSS properties i.e. font-size * becomes fontSize. This function takes a non-camelized text String and * returns the Camelized version. * * Takes aaa-bbb-ccc and returns aaaBbbCcc * * @param input * @return */ public static String camelize(String input) { StringBuffer output = new StringBuffer(); String check = camelized.get(input); if (check == null) { String[] temp = input.split("-"); output.append(temp[0].toLowerCase()); for (int loop = 1; loop < temp.length; loop++) { output.append(temp[loop].substring(0, 1).toUpperCase()); output.append(temp[loop].substring(1, temp[loop].length()).toLowerCase()); } camelized.put(input, output.toString()); unCamelized.put(output.toString(), input); return output.toString(); } else { return check; } } /** * * Takes aaaBbbCcc and returns aaaBbbCcc aaa-bbb-ccc * * @param input * @return */ public static String unCamelize(String input) { String output = ""; output = unCamelized.get(input); if (output == null) { StringBuffer out = new StringBuffer(); for (int loop=0; loop < input.length(); loop++){ char theChar = input.charAt(loop); if(Character.isUpperCase(theChar)){ out.append("-"); } out.append((""+theChar).toLowerCase()); } output = out.toString(); unCamelized.put(input, output); camelized.put(output, input); } return output; } /** * Simple HashMap to hold cached camelized strings. */ static HashMap<String, String> camelized = new HashMap<String, String>(); static HashMap<String, String> unCamelized = new HashMap<String, String>(); /** * Get the computed style for a particular CSS property. Currently gwt-fx * cannot cope if the computed style for any property turns out to be "auto" * so we throw an exception suggesting that the start value for the CSS * property needs to be defined. This method wraps around the JSNI method * getComputedStyleJSNI(). * * Added code to deal with "auto" if style is width or height to address IE issue identified in Issue 130. * Switched String equality checking as advised in comments by rkrysinski in Issue 130. * * @param element DOM element to query * @param styleProperty * @return */ public static String getComputedStyle(Element element, String styleProperty) { String sP = styleSheetImpl.correctStyleProperty(styleProperty); String result = styleSheetImpl.getComputedStyleJSNI(element, sP); if ("auto".equals(result)){ if(styleProperty=="width")result = getWidth(element); else if (styleProperty=="height")result = getHeight(element); else throw new RuntimeException("Computed style for " + styleProperty + " on Element "+element+" is auto - try setting a start value explicitly"); if (result.startsWith("undefined")) throw new RuntimeException("Computed style for " + styleProperty + " on Element "+element+" is undefined - try setting a start value explicitly"); } else if ("transparent".equals(result)||removeSpaces(result).equals("rgba(0,0,0,0)")) return getComputedStyle(element.getParentElement(), styleProperty); return result; } /** * Added as part of Issue 130 to address IE returning "auto" * @param element * @return */ private static native String getWidth(Element element)/*-{return element.offsetWidth+'px';}-*/; /** * Added as part of Issue 130 to address IE returning "auto" * @param element * @return */ private static native String getHeight(Element element)/*-{return element.offsetHeight+'px';}-*/; /** * Remove spaces from a String * * Null checking pre-condition added due to comment from rkrysinski in Issue 130. * * @param s * @return */ public static String removeSpaces(String s) { String t=""; if (s!=null){ String[] st = s.split(" "); for (int i =0;i < st.length; i++) t += st[i]; } return t; } /** * From a particular style property value return the unit type - which can be * one of the following: pt, px, em, % or nothing. * * @param stylePropertyValue * @return */ public static String getUnits(String stylePropertyValue) { if (stylePropertyValue.endsWith("pt")) return "pt"; if (stylePropertyValue.endsWith("px")) return "px"; if (stylePropertyValue.endsWith("em")) return "em"; if (stylePropertyValue.endsWith("%")) return "%"; return ""; } /** * Given a style property value with a potential unit (em, pt, px, %) suffix, * return the pure numeric value. * * @param stylePropertyValue input value. * @param unitOfStyleComponent numeric equivalent of input value * @return */ public static String getValue(String stylePropertyValue, String unitOfStyleComponent) { if (unitOfStyleComponent.equals("")) return stylePropertyValue; return stylePropertyValue.split(unitOfStyleComponent)[0]; } /** * Courtesy method to get value quicker * @param stylePropertyValue * @return */ public static String getValue(String stylePropertyValue){ return getValue(stylePropertyValue, getUnits(stylePropertyValue)); } public static boolean isClip(String style) { return style.contains("clip"); } public static boolean isColour(String style) { return ((style.contains("rgb(")) || (style.contains("rgba(")) || (style.contains("#")) || (style.contains("transparent"))); } public static boolean isScalar(String style, String value) { return ((value.contains("pt")) || (value.contains("px")) || (value.contains("em")) || (value.contains("%")) || (style.contains("opacity"))); } /** * Expect something of the form rect(a,b,c,d) * * @param clip value of form rect(a,b,c,d) * @return String array representing the top, right, bottom, left values in * that order. */ public static String[] parseClip(String clip) { String trbl[] = new String[4]; String[] param = clip.split("\\("); String[] params = param[1].split(","); if (params.length > 1) { trbl[0] = params[0].trim(); trbl[1] = params[1].trim(); trbl[2] = params[2].trim(); trbl[3] = params[3].split("\\)")[0].trim(); } else { // Someone's written in IE style. params = param[1].split(" "); trbl[0] = params[0].trim(); trbl[1] = params[1].trim(); trbl[2] = params[2].trim(); trbl[3] = params[3].split("\\)")[0].trim(); } return trbl; } /** * Checks if colour is transparent See issue #43 * * @param colour * @return */ public static boolean isTransparent(String colour) { return colour.equals("transparent"); } /** * Parse a string colour element into 3 individual integer rgb components * * @param input string. * @return integer array of 3 elements representing rgb values in that order. * @throws Exception Allows us to throw exception if a colour cannot be parsed. */ public static int[] parseColour(String hex) throws ColorException { int[] rgb = new int[4]; //Assume alpha component is not set. rgb[3] = -1; try { // Lets first see if we are dealing with a Hex string (i.e.does the string // start with a #) if (hex.startsWith("#")) { hex = hex.substring(1).trim(); if (hex.length() == 3) { rgb[0] = Integer.parseInt(hex.substring(0, 1) + hex.substring(0, 1), 16); rgb[1] = Integer.parseInt(hex.substring(1, 2) + hex.substring(1, 2), 16); rgb[2] = Integer.parseInt(hex.substring(2, 3) + hex.substring(2, 3), 16); } else if (hex.length() == 6) { rgb[0] = Integer.parseInt(hex.substring(0, 2), 16); rgb[1] = Integer.parseInt(hex.substring(2, 4), 16); rgb[2] = Integer.parseInt(hex.substring(4, 6), 16); } } else { // OK, not a hex string, the lets see if it is rgb(a,b,c) or a // rgba(a,b,c) String[] split1 = hex.split("\\("); if (split1.length > 1) { String[] split2 = (split1[1]).split(","); if (split2.length > 2) { String one = split2[0].trim(); String two = split2[1].trim(); String thr = split2[2].trim(); rgb[0] = Integer.parseInt(one); rgb[1] = Integer.parseInt(two); // Deal with alpha component if we have rgba(r,g,b,alpha) if (split1[0].equals("rgba")){ rgb[2] = Integer.parseInt(thr); String fth = split2[3].trim(); rgb[3] = (int)(Double.parseDouble(fth.split("\\)")[0])*100); } else{ rgb[2] = Integer.parseInt(thr.split("\\)")[0]); } } } else { // OK, so it must be a named color, i.e. lightyellow / green etc etc // BUT, there are 150 of these implemented as standard across a // browser // AND, including support for all those will bloat the code, so we'll // return an exception throw new ColorException("Unable to parse colour: " + hex); } } } catch (ColorException e) { rgb[0] = 255; rgb[1] = 0; rgb[2] = 0; rgb[3] = 100; throw e; } return rgb; } }