/** * Copyright 2010 Google Inc. * * 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.wave.splash.text; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import cc.kune.core.client.errors.DefaultException; import com.google.inject.Inject; import com.google.inject.Singleton; // TODO: Auto-generated Javadoc /** * A utility class that converts raw wave documents into html. * * @author dhanji@gmail.com (Dhanji R. Prasanna) */ @Singleton public class Markup { /** The Constant MONTH_DAY_FORMATTER. */ private static final DateFormat MONTH_DAY_FORMATTER = new SimpleDateFormat("MMM dd"); /** The Constant MONTH_DAY_YEAR_FORMATTER. */ private static final DateFormat MONTH_DAY_YEAR_FORMATTER = new SimpleDateFormat("MMM dd, yyyy"); /** The Constant TIME_FORMATTER. */ private static final DateFormat TIME_FORMATTER = new SimpleDateFormat("h:mm a"); /** The Constant TIME_MILLIS_FORMATTER. */ private static final DateFormat TIME_MILLIS_FORMATTER = new SimpleDateFormat("s.SSS"); /** * Embed snippet. * * @param waveId the wave id * @return the string */ public static String embedSnippet(final String waveId) { // TODO(dhanji): Move to template return "<script type=\"text/javascript\">" + " function load() {" + " var targetDiv = document.getElementById('waveframe');" + " var wavePanel = new google.wave.WavePanel({" + " rootUrl: \"http://localhost:8080/\"," + " target: targetDiv," + " lite: true" + " }).loadWave(\"" + waveId + "\");" + " }" + " </script>"; } /** * Extract scheme. * * @param uri the uri * @return the string */ private static String extractScheme(final String uri) { if (null == uri) { return null; } final int colonPos = uri.indexOf(':'); if (colonPos < 0) { return null; } final String scheme = uri.substring(0, colonPos); if (scheme.indexOf('/') >= 0 || scheme.indexOf('#') >= 0) { // The URI's prefix up to the first ':' contains other URI special // chars, and won't be interpreted as a scheme. return null; } return scheme; } /** * Formats a given timestamp into a friendly date string. The output will look * differently depending on the day and year. If the time given is the same * day as "now", then it will only display the time (12:30 PM). If it's in the * same year, then it will display the month and day (Jun 01) otherwise it * will return the month, day and year (Jun 01, 2009). * * @param timestamp the timestamp * @return the formatted date time string. */ public static String formatDateTime(final long timestamp) { final Date date = new Date(timestamp); final Date now = new Date(); if (now.getDay() == date.getDay()) { return TIME_FORMATTER.format(date); } else if (now.getYear() == date.getYear()) { return MONTH_DAY_FORMATTER.format(date); } else { return MONTH_DAY_YEAR_FORMATTER.format(date); } } /** * Format millis. * * @param millis the millis * @return the string */ public static String formatMillis(final long millis) { return TIME_MILLIS_FORMATTER.format(new Date(millis)) + "s"; } // TODO(dhanji): Should we allow more schemes? Like im: /** * Checks if is safe uri. * * @param uri the uri * @return true, if is safe uri */ private static boolean isSafeUri(final String uri) { final String scheme = extractScheme(uri); return (scheme == null || "http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme) || "mailto".equalsIgnoreCase(scheme) || "ftp".equalsIgnoreCase(scheme)); } /** * Checks if an image URL is safe (i.e. hosted on Google) * * @param imageUrl * A string URL * @return True if this image URL is safe to use in a google-hosted page. */ public static boolean isTrustedImageUrl(final String imageUrl) { final String scheme = extractScheme(imageUrl); // NOTE(dhanji): the trailing slash is extremely important. return (scheme != null) && (imageUrl.startsWith(scheme + "://www.google.com/") || imageUrl.startsWith(scheme + "://google.com/")); } /** * Sanitizes untrusted text so that it does not emit HTML markup. This utility * is based on a similar one in GWT's SafeHtml.java. It eliminates all * predefined entities in HTML/XHTML, replacing them with escape codes: * http:// * en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references * * @param text * The untrusted text to sanitize * @return Escaped text that can safely be rendered in a web page. */ public static String sanitize(final String text) { final StringBuilder out = new StringBuilder(text.length()); final char[] chars = text.toCharArray(); for (final char c : chars) { switch (c) { case '&': out.append("&"); break; case '\'': out.append("'"); break; case '"': out.append("""); break; case '<': out.append("<"); break; case '>': out.append(">"); break; default: // allow all other characters. out.append(c); } } return out.toString(); } /** * First sanitizes the given URI and then encodes it using the UTF-8 character * set. * * @param uri * An untrusted URI string * @return Sanitized, URL-encoded URI for embedding in HTML */ static String sanitizeAndEncode(final String uri) { try { return URLEncoder.encode(sanitizeUri(uri), "UTF-8"); } catch (final UnsupportedEncodingException e) { throw new DefaultException(e); } } /** * Checks if the scheme is one of four simple types (see #isSafeUri), if not * disallows the URI by reducing it to '#'. * * @param uri An untrusted URI string to sanitize * @return Returns a safe URI. */ private static String sanitizeUri(final String uri) { return isSafeUri(uri) ? uri : "#"; } /** * To blip id. * * @param id the id * @return the string */ public static String toBlipId(final String id) { // HACK to make blip ids work as DOM ids return id.replace('-', '+'); } // // public static ClientAction measure(String action, long searchTime) { // return new ClientAction("measure") // .html(action + " completed in " + Markup.formatMillis(searchTime)); // } /** * Simply converts a lowerCamelCased symbol into a dashed one: * * <pre> * fontWeight -> font-weight * </pre> * * TODO: perhaps we should intern all these strings to save memory and remove * the overhead of string allocation, they could also be perfectly hashed. * * @param name the name * @return the string */ static String toDashedStyle(final String name) { final char[] nameChars = name.toCharArray(); // Pre allocate buffer, +1 for '-' (2-dashes are uncommon) final StringBuilder builder = new StringBuilder(nameChars.length + 1); for (final char nameChar : nameChars) { if (Character.isUpperCase(nameChar)) { builder.append('-'); builder.append(Character.toLowerCase(nameChar)); continue; } builder.append(nameChar); } return builder.toString(); } /** * To dom id. * * @param id the id * @return the string */ public static String toDomId(final String id) { // HACK to make blip ids work as DOM ids return id.replace('+', '-'); } /** * Instantiates a new markup. */ @Inject Markup() { } /** * Gets the display name. * * @param participantId the participant id * @return the participant's display name. */ // TODO: This doesn't belong here. public String getDisplayName(final String participantId) { return "FIXME"; } /** * Gets the image size. * * @return the image size */ public String getImageSize() { return "33px"; } /** * Gets the image url. * * @param participantId the participant id * @return the participant's image url. */ // TODO: This doesn't belong here. public String getImageUrl(final String participantId) { // FIXME return "others/unknown.jpg"; } /** * Helpful utility for templates. * * @param text the text * @return the string */ public String sanitizeHtml(final String text) { return Markup.sanitize(text); } }