All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ioc.commons.impl.gwt.client.ui.Css3Util Maven / Gradle / Ivy

There is a newer version: 1.2.1
Show newest version
package org.ioc.commons.impl.gwt.client.ui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import org.ioc.commons.impl.gwt.client.ui.Css3Util.Css3EffectQueue.AppearEffectType;
import org.ioc.commons.impl.gwt.client.ui.Css3Util.Css3EffectQueue.DisappearEffectType;
import org.ioc.commons.impl.gwt.client.ui.Css3Util.Css3EffectQueue.EffectHandler;
import org.ioc.commons.ui.HasStorage;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.user.client.ui.Widget;

public class Css3Util {
	/*
	 * TODO: Controlar en toda la aplicación el efecto si CSS3 no está
	 * soportado.
	 */

	private static FormatterLogger logger = FormatterLogger.getLogger(Css3Util.class);

	public enum VisibilityModeStyle {
		USE_DISPLAY_PROPERTY, USE_VISIBILITY_PROPERTY;
	}

	public static Css3Style getStyle(Element element) {
		return new Css3Style(element);
	}

	public static Css3Toolkit getToolkit(Element element) {
		return getToolkit(element, null);
	}

	private static class Key {
		Element element;

		Key(Element element) {
			this.element = element;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((element == null) ? 0 : element.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}
			if (obj == null) {
				return false;
			}
			if (!(obj instanceof Key)) {
				return false;
			}
			Key other = (Key) obj;
			if (element == null) {
				if (other.element != null) {
					return false;
				}
			} else if (!element.equals(other.element)) {
				return false;
			}
			return true;
		}
	}

	public static Css3Toolkit getToolkit(Element element, HasStorage storage) {
		Css3Toolkit css3Toolkit = null;
		if (storage != null) {
			Key key = new Key(element);
			css3Toolkit = (Css3Toolkit) (storage != null ? storage.retrieve(key) : null);
			if (css3Toolkit == null) {
				css3Toolkit = new Css3Toolkit(element);
				storage.store(key, css3Toolkit);
			}
		} else {
			css3Toolkit = new Css3Toolkit(element);
		}
		return css3Toolkit;
	}

	public static Css3Toolkit getToolkit(final Widget w) {
		return getToolkit(w, null);
	}

	public static Css3Toolkit getToolkit(final Widget w, HasStorage storage) {
		Css3Toolkit tk = getToolkit(w.getElement(), storage);
		if (!w.isAttached() && !tk.attachHandlerAdded) {
			/**
			 * Si el widget no está attachado, los efectos no dispararán el
			 * TransactionListener. Por eso justo cuando se attache, se dispara
			 * y vamos al siguiente.
			 */
			w.addAttachHandler(new AttachEvent.Handler() {

				@Override
				public void onAttachOrDetach(AttachEvent event) {
					getToolkit(w.getElement()).css3EffectQueue.resolvePendingEffects();
				}
			});
			tk.attachHandlerAdded = true;
		}
		return tk;
	}

	protected static void getDefaultVisibleDisplay(Element elm) {
		if (elm.getTagName().equalsIgnoreCase("table")) {
			elm.getStyle().setProperty("display", "table");
		} else if (elm.getTagName().equalsIgnoreCase("td")) {
			elm.getStyle().setProperty("display", "table-cell");
		} else if (elm.getTagName().equalsIgnoreCase("tr")) {
			elm.getStyle().setProperty("display", "table-row");
		} else {
			elm.getStyle().setDisplay(Display.INLINE);
		}

	}

	public static class Css3Style {
		private static final String WEBKIT = "webkit";
		private static final String MOZILLA = "moz";
		private static final String OPERA = "o";
		private static final String MICROSOFT = "ms";

		private final Style style;
		private final Element element;

		Css3Style(Element element) {
			if (element == null) {
				throw new NullPointerException("Element cannot be null");
			}
			this.element = element;
			this.style = element.getStyle();
		}

		protected int getIntProperty(String name) {
			String value = getProperty(name);
			if (value != null && !value.isEmpty()) {
				double number = JsUtil.extractNumberValue(value);
				if (value.endsWith("s") || !value.endsWith("ms")) {
					number *= 1000.0;
				}

				return (int) number;
			}
			return 0;
		}

		protected String getProperty(String name) {
			String result = null;
			/**
			 * Camel case
			 */
			result = this.style.getProperty(name);
			result = (result == null || result.isEmpty()) ? this.style.getProperty(WEBKIT + Format.capitalize(name))
					: result;
			result = (result == null || result.isEmpty()) ? this.style.getProperty(MOZILLA + Format.capitalize(name))
					: result;
			result = (result == null || result.isEmpty()) ? this.style.getProperty(OPERA + Format.capitalize(name))
					: result;
			result = (result == null || result.isEmpty()) ? this.style.getProperty(MICROSOFT + Format.capitalize(name))
					: result;

			// /**
			// * No camel case
			// */
			//
			// result = getRawProperty(this.style, name);
			// result = (result == null || result.isEmpty()) ?
			// this.style.getProperty(WEBKIT + Format.capitalize(name))
			// : result;
			// result = (result == null || result.isEmpty()) ?
			// this.style.getProperty(MOZILLA + Format.capitalize(name))
			// : result;
			// result = (result == null || result.isEmpty()) ?
			// this.style.getProperty(OPERA + Format.capitalize(name))
			// : result;
			// result = (result == null || result.isEmpty()) ?
			// this.style.getProperty(MICROSOFT + Format.capitalize(name))
			// : result;

			return result;
		}

		protected void setProperty(String name, String value) {
			this.style.setProperty(name, value);
			this.style.setProperty(WEBKIT + Format.capitalize(name), value);
			this.style.setProperty(MOZILLA + Format.capitalize(name), value);
			this.style.setProperty(OPERA + Format.capitalize(name), value);
			this.style.setProperty(MICROSOFT + Format.capitalize(name), value);
		}

		public void setTransform(String value) {
			setProperty("transform", value);
		}

		public void setTransition(String value) {
			setProperty("transition", value);
		}

		public void setTransitionDuration(String value) {
			setProperty("transitionDuration", value);
		}

		public void setTransitionProperty(String value, boolean add) {
			if (!add) {
				setProperty("transitionProperty", value);
			} else {
				HashSet tpSet = new HashSet();
				tpSet.add(value);

				String tp = getProperty("transitionProperty");
				String[] parts = (tp != null) ? tp.split(",") : null;
				if (parts != null) {
					List tpList = Arrays.asList(parts);
					tpSet.addAll(tpList);
				}

				StringBuffer sb = new StringBuffer();
				for (String newTps : tpSet) {
					sb.append(newTps).append(',');
				}

				if (sb.charAt(sb.length() - 1) == ',') {
					sb.deleteCharAt(sb.length() - 1);
				}

				setProperty("transitionProperty", sb.toString());
			}
		}

		public void removeTransitionProperty(String property) {
			HashSet tpSet = new HashSet();

			String tp = getProperty("transitionProperty");
			String[] parts = (tp != null) ? tp.split(",") : null;
			String[] properties = property.split(",");
			if (parts != null) {
				for (String part : parts) {
					boolean remove = false;
					for (String prop : properties) {
						if (part.trim().equalsIgnoreCase(prop.trim())) {
							remove = true;
							break;
						}
					}

					if (!remove) {
						tpSet.add(part);
					}
				}
			}

			StringBuffer sb = new StringBuffer();
			if (!tpSet.isEmpty()) {
				for (String newTps : tpSet) {
					sb.append(newTps).append(',');
				}

				if (sb.charAt(sb.length() - 1) == ',') {
					sb.deleteCharAt(sb.length() - 1);
				}
			}

			setProperty("transitionProperty", sb.toString());
		}

		public int getTransitionDuration() {
			int td = getIntProperty("transitionDuration");

			return td;
		}

		public void setTransitionTimingFunction(String value) {
			setProperty("transitionTimingFunction", value);
		}

		public void addTransitionEndListener(TransitionEndListener transitionEndListener) {
			this.addTransitionEndListener(element, transitionEndListener);
		}

		public void removeTransitionEndListener(TransitionEndListener transitionEndListener) {
			this.removeTransitionEndListener(element, transitionEndListener.callback);
		}

		protected native void addTransitionEndListener(Element element,
				final TransitionEndListener transitionEndListener) /*-{
																	var callBack = function(e) {
																	transitionEndListener.@org.ioc.commons.impl.gwt.client.ui.Css3Util.TransitionEndListener::onTransitionEnd(Lcom/google/gwt/dom/client/NativeEvent;)(e);
																	}

																	//Chrome
																	element.addEventListener('webkitTransitionEnd', callBack, false);
																	//Firefox y estandar W3C
																	element.addEventListener('transitionend', callBack, false);
																	//IE10+
																	element.addEventListener('msTransitionEnd', callBack, false);
																	//Opera
																	element.addEventListener('oTransitionEnd', callBack, false);
																	//Standard
																	element.addEventListener('transitionEnd', callBack, false);

																	transitionEndListener.@org.ioc.commons.impl.gwt.client.ui.Css3Util.TransitionEndListener::setCallback(Lcom/google/gwt/core/client/JavaScriptObject;)(callBack);

																	}-*/;

		protected native void removeTransitionEndListener(Element element, JavaScriptObject callBack) /*-{
																										//Chrome
																										element.removeEventListener('webkitTransitionEnd', callBack, false);
																										//Firefox y estandar W3C
																										element.removeEventListener('transitionend', callBack, false);
																										//IE10+
																										element.removeEventListener('msTransitionEnd', callBack, false);
																										//Opera
																										element.removeEventListener('oTransitionEnd', callBack, false);
																										//Standard
																										element.removeEventListener('transitionEnd', callBack, false);
																										}-*/;

	}

	public static class Css3Toolkit {
		boolean attachHandlerAdded;
		private Element theElement;
		private Css3EffectQueue css3EffectQueue;

		Css3Toolkit(Widget w) {
			this(w.getElement());
		}

		Css3Toolkit(Element element) {
			this.theElement = element;
			this.css3EffectQueue = new Css3EffectQueue();
		}

		public static EffectHandler[] disappear(VisibilityModeStyle visibilityModeStyle, Widget... widgets) {
			return disappear(DisappearEffectType.FADE_OUT, visibilityModeStyle, widgets);
		}

		public static EffectHandler[] disappear(DisappearEffectType disappearEffect,
				VisibilityModeStyle visibilityModeStyle, Widget... widgets) {
			EffectHandler[] ehs = new EffectHandler[widgets.length];
			for (int i = 0; i < widgets.length; i++) {
				Widget widget = widgets[i];
				EffectHandler eh = Css3Util.getToolkit(widget).disappear(disappearEffect, visibilityModeStyle);
				ehs[i] = eh;
			}

			return ehs;
		}

		public static EffectHandler[] appear(Widget... widgets) {
			return appear(AppearEffectType.FADE_IN, widgets);
		}

		public static EffectHandler[] appear(AppearEffectType appearEffect, Widget... widgets) {
			EffectHandler[] ehs = new EffectHandler[widgets.length];
			for (int i = 0; i < widgets.length; i++) {
				Widget widget = widgets[i];
				EffectHandler eh = Css3Util.getToolkit(widget).appear(appearEffect);
				ehs[i] = eh;
			}
			return ehs;
		}

		public EffectHandler disappear(VisibilityModeStyle visibilityModeStyle) {
			return disappear(DisappearEffectType.FADE_OUT, visibilityModeStyle);
		}

		public EffectHandler disappear(final DisappearEffectType disappearEffect,
				final VisibilityModeStyle visibilityModeStyle) {
			return disappear(disappearEffect, visibilityModeStyle, 1000);
		}

		public EffectHandler disappear(final DisappearEffectType disappearEffect,
				final VisibilityModeStyle visibilityModeStyle, int durationMs) {
			return this.css3EffectQueue.disappear(theElement, disappearEffect, visibilityModeStyle, durationMs);
		}

		public EffectHandler appear() {
			return appear(AppearEffectType.FADE_IN);
		}

		public EffectHandler appear(AppearEffectType appearEffect) {
			return appear(appearEffect, 1000);
		}

		public EffectHandler appear(AppearEffectType appearEffect, int durationMs) {
			return this.css3EffectQueue.appear(theElement, appearEffect, durationMs);
		}

		public EffectHandler resize(String width, String height) {
			return resize(width, height, 1000);
		}

		public EffectHandler resize(String width, String height, int durationMs) {
			return this.css3EffectQueue.resize(theElement, width, height, durationMs);
		}

		public boolean isAppeared() {
			return this.css3EffectQueue.isAppeared(theElement);
		}

		public void disableTraceLog() {
			this.css3EffectQueue.disableTraceLog();
		}

		public void enableTraceLog(String traceElementId) {
			this.css3EffectQueue.enableTraceLog(traceElementId);
		}

	}

	public static class Css3EffectQueue {
		private boolean traceLog;

		private enum EffectType {
			APPEAR, DISAPPEAR, HIDE, RESIZE;
		}

		public class EffectHandler {
			private Css3Effect pe;

			protected EffectHandler(Css3Effect pe) {
				this.pe = pe;
			}

			/*
			 * Esta clase está pensada para ir añadiendo métodos con los efectos
			 * y poder anexar efectos que en lugar de querer que se ejecuten en
			 * cascada, se ejecuten en paralelo.
			 * 
			 * Estos método nunca llevaran el tiempo de duracción. Siempre se
			 * ejecutarán en el mismo que su "patrón" o efecto al que están
			 * enlazado y ejecutarán en paralelo. De esto modo no hay que
			 * complicar la lógica del evento de fin de transicción para saber
			 * cuando comenzar el siguiente en la cola.
			 */
			public EffectHandler appear(AppearEffectType appearEffect) {

				Css3Effect attchFx = newAppearEffect(appearEffect, this.pe.duration, this.pe.cs);

				if (this.pe.attachedEffects == null) {
					this.pe.attachedEffects = new ArrayList();
				}

				this.pe.attachedEffects.add(attchFx);

				return this;
			}

			public EffectHandler disappear(DisappearEffectType disappearEffect, VisibilityModeStyle visibilityModeStyle) {
				Css3Effect attchFx = newDisappearEffect(disappearEffect, this.pe.duration, this.pe.cs);

				if (this.pe.attachedEffects == null) {
					this.pe.attachedEffects = new ArrayList();
				}

				this.pe.attachedEffects.add(attchFx);

				return this;
			}

			public Css3Style getCss3Style() {
				return pe.cs;
			}

		}

		protected class Css3Effect {
			protected EffectType et;
			protected DisappearEffectType de;
			protected AppearEffectType ae;
			protected String width;
			protected String height;
			protected VisibilityModeStyle vms;
			protected int duration = 1000;
			protected Css3Style cs;
			protected List attachedEffects;
		}

		private final List pendingEffects = new ArrayList();

		private class ProcessNextPendingEffect extends TransitionEndListener {

			private EffectType currentEffectType;
			private boolean traceLog;
			private String traceElementId;

			public ProcessNextPendingEffect(EffectType currentEffectType, boolean traceLog, String traceElementId) {
				this.currentEffectType = currentEffectType;
				this.traceLog = traceLog;
				this.traceElementId = traceElementId;
			}

			@Override
			public void onTransitionEnd(NativeEvent e) {
				logger.info(traceLog, "{0}: Resuelto efecto {1}", traceElementId, currentEffectType);

				Element elm = (Element.is(e.getEventTarget()) ? Element.as(e.getEventTarget()) : null);

				this.removeTransitionEndListener(elm);
				resolvePendingEffects();
			}
		}

		public enum DisappearEffectType {
			FADE_OUT, MOVE_TO_LEFT_TOP, MOVE_TO_TOP, MOVE_TO_RIGHT_TOP, MOVE_TO_RIGHT, MOVE_TO_RIGHT_BOTTOM, MOVE_TO_BOTTOM, MOVE_TO_LEFT_BOTTOM, MOVE_TO_LEFT;
		}

		public enum AppearEffectType {
			FADE_IN, MOVE_FROM_LEFT_TOP, MOVE_FROM_TOP, MOVE_FROM_RIGHT_TOP, MOVE_FROM_RIGHT, MOVE_FROM_RIGHT_BOTTOM, MOVE_FROM_BOTTOM, MOVE_FROM_LEFT_BOTTOM, MOVE_FROM_LEFT;
		}

		private boolean resolvingPendingEffects;
		private Css3Effect resolvingThis;
		private String traceElementId;
		private TransitionEndListener lastTransitionEndListener;
		private boolean autoRemoveLastTransitionEndListener;

		public Css3EffectQueue() {
			// Nada
		}

		protected void addPendingEffect(Css3Effect pedingEffect) {
			logger.info(traceLog, "{0}: Añadiendo efecto {1}. Pendientes: {2}", this.traceElementId, pedingEffect.et,
					this.pendingEffects.size());

			this.pendingEffects.add(pedingEffect);

			if (!this.resolvingPendingEffects) {
				this.resolvingPendingEffects = true;

				logger.info(traceLog, "{0}: Iniciando solucionador de efectos...", traceElementId);
				Scheduler.get().scheduleDeferred(new ScheduledCommand() {

					@Override
					public void execute() {
						logger.info(traceLog, "{0}: Comenzando a resolver efectos...", traceElementId);
						resolvePendingEffects();
					}
				});
			} else {
				logger.info(traceLog, "{0}: Esperamos. Actualmente resolviendo efecto pendiente: {1}...",
						traceElementId, (this.resolvingThis != null) ? String.valueOf(this.resolvingThis.et)
								: "-- ninguno --");
			}
		}

		protected void resolvePendingEffects() {
			if (!this.pendingEffects.isEmpty()) {
				this.resolvingThis = this.pendingEffects.remove(0);
				logger.info(traceLog, " {0}: Siguiente efecto a resolver {1}", traceElementId, this.resolvingThis.et);
				resolveEffect(resolvingThis, true);
			} else {
				this.resolvingPendingEffects = false;
				this.resolvingThis = null;

				if (this.lastTransitionEndListener != null) {
					this.lastTransitionEndListener.onTransitionEnd(null);

					if (this.autoRemoveLastTransitionEndListener) {
						this.lastTransitionEndListener = null;
					}
				}

				logger.info(traceLog, " {0}: No hay más efectos pendientes", traceElementId);
			}
		}

		protected void resolveEffect(Css3Effect pe, boolean proccessNext) {
			logger.info(traceLog, "{0}: Resolviendo efecto {1}. Pendientes {2}", this.traceElementId, pe.et,
					pendingEffects.size());

			switch (pe.et) {
			case APPEAR:
				resolveAppear(pe.cs, pe.ae, pe.duration, proccessNext);
				break;
			case DISAPPEAR:
				resolveDisappear(pe.cs, pe.de, pe.duration, proccessNext);
				break;
			case HIDE:
				resolveHide(pe.cs, pe.vms, proccessNext);
				break;
			case RESIZE:
				resolveResize(pe.cs, pe.width, pe.height, pe.duration, proccessNext);
				break;
			}

			if (pe.attachedEffects != null) {
				for (Css3Effect attchFx : pe.attachedEffects) {
					resolveEffect(attchFx, false);
				}
			}
		}

		protected void resolveResize(final Css3Style css3Style, final String width, final String height, int duration,
				final boolean proccessNext) {
			final Style style = css3Style.element.getStyle();

			getDefaultVisibleDisplay(css3Style.element);
			style.setVisibility(com.google.gwt.dom.client.Style.Visibility.VISIBLE);

			if (width != null) {
				css3Style.setTransitionProperty("width", true);
				css3Style.setTransitionDuration(duration + "ms");
				css3Style.setTransitionTimingFunction("ease-in-out");

			}

			if (height != null) {
				css3Style.setTransitionProperty("height", true);
				css3Style.setTransitionDuration(duration + "ms");
				css3Style.setTransitionTimingFunction("ease-in-out");
			}

			Scheduler.ScheduledCommand effectCommand = new Scheduler.ScheduledCommand() {

				@Override
				public void execute() {
					boolean resolveWithListener = false;

					/*
					 * Ancho
					 */
					if (width != null) {
						String curWidth = style.getProperty("width");
						int curOffsetWidth = css3Style.element.getOffsetWidth();

						if (width.equals(curWidth)
								|| (!width.contains("%") && JsUtil.extractNumberValue(width) == curOffsetWidth)) {
							logger.info(traceLog, "{0}: Ancho {1}=={2} ó {1}=={3} (no se aplicarán cambios).",
									traceElementId, width, curWidth, curOffsetWidth);
							resolveWithListener |= false;
						} else {

							resolveWithListener |= true;

						}
					}

					/*
					 * Alto
					 */
					if (height != null) {
						String curHeight = style.getHeight();
						int curOffsetHeight = css3Style.element.getOffsetHeight();

						if (height.equals(curHeight)
								|| (!height.contains("%") && JsUtil.extractNumberValue(height) == curOffsetHeight)) {
							logger.info(traceLog, "{0}: Alto {1}=={2} ó {1}=={3} (no se aplicarán cambios).",
									traceElementId, height, curHeight, curOffsetHeight);
							resolveWithListener |= false;
						} else {

							resolveWithListener |= true;
						}
					}

					/*
					 * Procesando
					 */
					if (proccessNext) {
						if (!resolveWithListener) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.RESIZE);

							resolvePendingEffects();
						} else {
							css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.RESIZE,
									traceLog, traceElementId));
						}
					}

					if (width != null) {
						style.setProperty("width", width);
					}

					if (height != null) {
						style.setProperty("height", height);
					}
				}
			};

			Scheduler.get().scheduleDeferred(effectCommand);
		}

		protected void resolveAppear(final Css3Style css3Style, final AppearEffectType appearEffect,
				final int duration, final boolean mainEffect) {
			Element parent = css3Style.element.getParentElement();
			final Style style = css3Style.element.getStyle();
			getDefaultVisibleDisplay(css3Style.element);
			style.setVisibility(com.google.gwt.dom.client.Style.Visibility.VISIBLE);

			Scheduler.ScheduledCommand effectCommand = null;

			logger.info(traceLog, "{0}: Resolviendo appear: Efecto {1}; ¿Es principal? {2}", traceElementId,
					appearEffect, mainEffect ? "SÍ" : "NO");

			switch (appearEffect) {
			case FADE_IN:
				if (mainEffect) {
					css3Style.setTransitionProperty("opacity", true);
					css3Style.setTransitionDuration("0s");
					style.setOpacity(0.0);
				}
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						css3Style.setTransitionProperty("opacity", true);
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.APPEAR,
									traceLog, traceElementId));
						}

						style.setOpacity(1.0);
					}
				};
				break;
			case MOVE_FROM_BOTTOM: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int top = (parent != null) ? parent.getClientHeight() : css3Style.element.getClientHeight();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(0.0, Unit.PX);
					style.setTop(top, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_TOP: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");
					int top = (parent != null) ? -parent.getClientHeight() : -css3Style.element.getClientHeight();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(0.0, Unit.PX);
					style.setTop(top, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_LEFT: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int left = (parent != null) ? -parent.getClientWidth() : -css3Style.element.getClientWidth();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(left, Unit.PX);
					style.setTop(0.0, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_RIGHT: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int left = (parent != null) ? parent.getClientWidth() : css3Style.element.getClientWidth();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(left, Unit.PX);
					style.setTop(0.0, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_LEFT_BOTTOM: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int left = (parent != null) ? -parent.getClientWidth() : -css3Style.element.getClientWidth();
					int top = (parent != null) ? parent.getClientHeight() : css3Style.element.getClientHeight();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(left, Unit.PX);
					style.setTop(top, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_LEFT_TOP: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int left = (parent != null) ? -parent.getClientWidth() : -css3Style.element.getClientWidth();
					int top = (parent != null) ? -parent.getClientHeight() : -css3Style.element.getClientHeight();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(left, Unit.PX);
					style.setTop(top, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_RIGHT_BOTTOM: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int left = (parent != null) ? parent.getClientWidth() : css3Style.element.getClientWidth();
					int top = (parent != null) ? parent.getClientHeight() : css3Style.element.getClientHeight();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(left, Unit.PX);
					style.setTop(top, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			case MOVE_FROM_RIGHT_TOP: {
				if (mainEffect) {
					css3Style.setTransitionProperty("left, top", true);
					css3Style.setTransitionDuration("0s");

					int left = (parent != null) ? parent.getClientWidth() : css3Style.element.getClientWidth();
					int top = (parent != null) ? -parent.getClientHeight() : -css3Style.element.getClientHeight();
					style.setPosition(Position.RELATIVE);

					if (parent != null) {
						parent.getStyle().setOverflow(Overflow.HIDDEN);
					}
					style.setLeft(left, Unit.PX);
					style.setTop(top, Unit.PX);
				}
				effectCommand = getAppearMoveEffectCommand(css3Style, style, duration, mainEffect);
				break;
			}
			}

			Scheduler.get().scheduleDeferred(effectCommand);
		}

		protected void resolveDisappear(final Css3Style css3Style, final DisappearEffectType disappearEffect,
				final int duration, final boolean mainEffect) {
			final Element parent = css3Style.element.getParentElement();
			Scheduler.ScheduledCommand effectCommand = null;

			switch (disappearEffect) {
			case FADE_OUT:
				if (mainEffect) {
					css3Style.setTransitionTimingFunction("ease-in-out");
					css3Style.setTransitionProperty("opacity", true);
					css3Style.setTransitionDuration(duration + "ms");
				}
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("opacity", true);
							css3Style.setTransitionProperty("left, top", true);
						}
						
						double currentOpacity = JsUtil.extractNumberValue(getStyleProperty(css3Style.element, "opacity"));
						/*
						 * Como no cambie nada el efecto el evento endTransition
						 * no se lanza y perdemos seguir con el siguiente efecto
						 */
						if (currentOpacity != 0.0) {
							if (mainEffect) {
								ProcessNextPendingEffect transitionEndListener = new ProcessNextPendingEffect(
										EffectType.DISAPPEAR, traceLog, traceElementId);
								css3Style.addTransitionEndListener(transitionEndListener);
							}
							css3Style.element.getStyle().setOpacity(0.0);
						} else {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						}
					}
				};
				break;
			case MOVE_TO_LEFT_BOTTOM:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}

						double left = (parent != null) ? -parent.getOffsetWidth() : -css3Style.element.getOffsetWidth();
						double top = (parent != null) ? parent.getOffsetHeight() : css3Style.element.getOffsetHeight();

						double curLeft = css3Style.element.getOffsetLeft();
						double curTop = css3Style.element.getOffsetTop();

						if (curLeft == left && curTop == top) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {

								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setLeft(left, Unit.PX);
							css3Style.element.getStyle().setTop(top, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_BOTTOM:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}

						int top = (parent != null) ? parent.getOffsetHeight() : css3Style.element.getOffsetHeight();
						int curTop = css3Style.element.getOffsetTop();

						if (curTop == top) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {

								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setTop(top, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_LEFT:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}
						int left = (parent != null) ? -parent.getOffsetWidth() : -css3Style.element.getOffsetWidth();
						int curLeft = css3Style.element.getOffsetLeft();
						if (curLeft == left) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {

								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}

							css3Style.element.getStyle().setLeft(left, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_LEFT_TOP:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}
						int left = (parent != null) ? -parent.getOffsetWidth() : -css3Style.element.getOffsetWidth();
						int top = (parent != null) ? -parent.getOffsetHeight() : -css3Style.element.getOffsetHeight();

						int curLeft = css3Style.element.getOffsetLeft();
						int curTop = css3Style.element.getOffsetTop();

						if (curLeft == left && curTop == top) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {

								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setLeft(left, Unit.PX);
							css3Style.element.getStyle().setTop(top, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_RIGHT:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}
						int left = (parent != null) ? parent.getOffsetWidth() : css3Style.element.getOffsetWidth();
						int curLeft = css3Style.element.getOffsetLeft();
						if (curLeft == left) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {

								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setLeft(left, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_RIGHT_BOTTOM:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}
						int left = (parent != null) ? parent.getOffsetWidth() : css3Style.element.getOffsetWidth();
						int top = (parent != null) ? parent.getOffsetHeight() : css3Style.element.getOffsetHeight();
						int curLeft = css3Style.element.getOffsetLeft();
						int curTop = css3Style.element.getOffsetTop();
						if (curLeft == left && curTop == top) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {

								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setLeft(left, Unit.PX);
							css3Style.element.getStyle().setTop(top, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_RIGHT_TOP:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}
						int left = (parent != null) ? parent.getOffsetWidth() : css3Style.element.getOffsetWidth();
						int top = (parent != null) ? -parent.getOffsetHeight() : -css3Style.element.getOffsetHeight();
						int curLeft = css3Style.element.getOffsetLeft();
						int curTop = css3Style.element.getOffsetTop();
						if (curLeft == left && curTop == top) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {
								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setLeft(left, Unit.PX);
							css3Style.element.getStyle().setTop(top, Unit.PX);
						}
					}
				};
				break;
			case MOVE_TO_TOP:
				configDisappearMoveProperties(css3Style, parent, mainEffect);
				effectCommand = new Scheduler.ScheduledCommand() {

					@Override
					public void execute() {
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.setTransitionProperty("left, top", true);
						}
						int top = (parent != null) ? -parent.getOffsetHeight() : -css3Style.element.getOffsetHeight();
						int curTop = css3Style.element.getOffsetTop();
						if (curTop == top) {
							logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
									EffectType.DISAPPEAR);
							resolvePendingEffects();
						} else {
							if (mainEffect) {
								css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.DISAPPEAR,
										traceLog, traceElementId));
							}
							css3Style.element.getStyle().setTop(top, Unit.PX);
						}
					}
				};
				break;
			default:
				throw new IllegalArgumentException("Efecto de desaparición no entendido por el código: "
						+ disappearEffect);
			}

			Scheduler.get().scheduleDeferred(effectCommand);
		}

		protected void resolveHide(final Css3Style css3Style, final VisibilityModeStyle visibilityModeStyle,
				boolean proccessNext) {
			if (visibilityModeStyle == VisibilityModeStyle.USE_DISPLAY_PROPERTY) {
				css3Style.element.getStyle().setDisplay(Display.NONE);
			} else {
				css3Style.element.getStyle().setVisibility(com.google.gwt.dom.client.Style.Visibility.HIDDEN);
			}

			resolvePendingEffects();
		}

		private void configDisappearMoveProperties(final Css3Style css3Style, Element parent, boolean mainEffect) {
			if (parent != null) {
				parent.getStyle().setOverflow(Overflow.HIDDEN);
			}
			if (mainEffect) {
				css3Style.setTransitionDuration("0s");
				css3Style.element.getStyle().setLeft(0.0, Unit.PX);
				css3Style.element.getStyle().setTop(0.0, Unit.PX);
			}
			css3Style.element.getStyle().setPosition(Position.RELATIVE);
		}

		private ScheduledCommand getAppearMoveEffectCommand(final Css3Style css3Style, final Style style,
				final int duration, final boolean mainEffect) {
			return new ScheduledCommand() {

				@Override
				public void execute() {
					double left = JsUtil.extractNumberValue(style.getLeft());
					double top = JsUtil.extractNumberValue(style.getTop());

					if (left == 0.0 && top == 0.0) {
						logger.info(traceLog, "{0}: Resuelto efecto {1} sin aplicar cambios.", traceElementId,
								EffectType.APPEAR);
						if (mainEffect) {
							resolvePendingEffects();
						}
					} else {
						css3Style.setTransitionProperty("left, top", true);
						if (mainEffect) {
							css3Style.setTransitionDuration(duration + "ms");
							css3Style.setTransitionTimingFunction("ease-in-out");
							css3Style.addTransitionEndListener(new ProcessNextPendingEffect(EffectType.APPEAR,
									traceLog, traceElementId));
						}

						style.setLeft(0.0, Unit.PX);
						style.setTop(0.0, Unit.PX);
					}
				}
			};
		}

		public EffectHandler disappear(Widget widget, final DisappearEffectType disappearEffect,
				final VisibilityModeStyle visibilityModeStyle, int durationMs) {
			return disappear(widget.getElement(), disappearEffect, visibilityModeStyle, durationMs);
		}

		public EffectHandler disappear(Element theElement, final DisappearEffectType disappearEffect,
				final VisibilityModeStyle visibilityModeStyle, int durationMs) {
			Css3Style css3Style = Css3Util.getStyle(theElement);

			Css3Effect disappear = newDisappearEffect(disappearEffect, durationMs, css3Style);

			Css3Effect hide = newHideEffect(visibilityModeStyle, css3Style);

			addPendingEffect(disappear);
			addPendingEffect(hide);

			return new EffectHandler(/* el último */hide);
		}

		public EffectHandler appear(Widget widget, AppearEffectType appearEffect, int durationMs) {
			return this.appear(widget.getElement(), appearEffect, durationMs);
		}

		public EffectHandler appear(Element theElement, AppearEffectType appearEffect, int durationMs) {
			Css3Style css3Style = Css3Util.getStyle(theElement);

			Css3Effect appear = newAppearEffect(appearEffect, durationMs, css3Style);

			addPendingEffect(appear);

			return new EffectHandler(appear);
		}

		public EffectHandler resize(Widget widget, String width, String height, int durationMs) {
			return resize(widget.getElement(), width, height, durationMs);
		}

		public EffectHandler resize(Element theElement, String width, String height, int durationMs) {
			Css3Style css3Style = Css3Util.getStyle(theElement);

			Css3Effect resize = newResizeEffect(width, height, durationMs, css3Style);

			addPendingEffect(resize);
			return new EffectHandler(resize);
		}

		public boolean isAppeared(Element theElement) {
			for (int i = pendingEffects.size() - 1; i >= 0; i--) {
				switch (pendingEffects.get(i).et) {
				case APPEAR:
					return true;
				case DISAPPEAR:
					return false;
				case HIDE:
					return false;
				default:
					// Sigue buscando
				}
			}

			if (this.resolvingThis != null) {
				switch (this.resolvingThis.et) {
				case APPEAR:
					return true;
				case DISAPPEAR:
					return false;
				case HIDE:
					return false;
				default:
					// Sigue buscando
				}
			}

			Style style = theElement.getStyle();
			String strOpacity = style.getOpacity();
			double opacity = (strOpacity != null && !strOpacity.isEmpty()) ? JsUtil.extractNumberValue(strOpacity)
					: 1.0;
			String display = style.getDisplay();
			String visibility = style.getVisibility();
			boolean appeared = (opacity == 1.0) && !("none".equals(display)) && !("hidden".equals(visibility));
			return appeared;
		}

		public boolean isTraceLog() {
			return traceLog;
		}

		public void disableTraceLog() {
			this.traceLog = false;
		}

		public void enableTraceLog(String traceElementId) {
			this.traceLog = true;
			this.traceElementId = traceElementId;
		}

		public void cancelPending() {
			logger.info(traceLog, " {0}: Cancelando todos los efectos pendientes", traceElementId);
			this.pendingEffects.clear();
			this.resolvingPendingEffects = false;
			this.resolvingThis = null;
		}

		public void setLastTransitionEndListener(boolean autoRemove, TransitionEndListener lastTransitionEndListener) {
			this.lastTransitionEndListener = lastTransitionEndListener;
			this.autoRemoveLastTransitionEndListener = autoRemove;
		}

		private Css3Effect newAppearEffect(AppearEffectType appearEffect, int durationMs, Css3Style css3Style) {
			Css3Effect appear = new Css3Effect();
			appear.et = EffectType.APPEAR;
			appear.ae = appearEffect;
			appear.cs = css3Style;
			appear.duration = durationMs;
			return appear;
		}

		private Css3Effect newResizeEffect(String width, String height, int durationMs, Css3Style css3Style) {
			Css3Effect resize = new Css3Effect();
			resize.et = EffectType.RESIZE;
			resize.width = width;
			resize.height = height;
			resize.cs = css3Style;
			resize.duration = durationMs;
			return resize;
		}

		private Css3Effect newHideEffect(final VisibilityModeStyle visibilityModeStyle, Css3Style css3Style) {
			Css3Effect hide = new Css3Effect();
			hide.et = EffectType.HIDE;
			hide.vms = visibilityModeStyle;
			hide.cs = css3Style;
			return hide;
		}

		private Css3Effect newDisappearEffect(final DisappearEffectType disappearEffect, int durationMs,
				Css3Style css3Style) {
			Css3Effect disappear = new Css3Effect();
			disappear.et = EffectType.DISAPPEAR;
			disappear.de = disappearEffect;
			disappear.cs = css3Style;
			disappear.duration = durationMs;
			return disappear;
		}

	}

	public static abstract class TransitionEndListener {
		private JavaScriptObject callback;

		public abstract void onTransitionEnd(NativeEvent e);

		@SuppressWarnings("unused")
		final private void setCallback(JavaScriptObject callback) {
			this.callback = callback;
		}

		final public void removeTransitionEndListener(Element element) {
			if (element != null) {
				Css3Util.getStyle(element).removeTransitionEndListener(this);
			}
		}
	}

	public static native String getStyleProperty(Element el, String prop) /*-{
	    var computedStyle;

	    if (document.defaultView && document.defaultView.getComputedStyle) { // standard (includes ie9)
	        computedStyle = document.defaultView.getComputedStyle(el, null)[prop];
	
	    } else if (el.currentStyle) { // IE older
	        computedStyle = el.currentStyle[prop];
	
	    } else { // inline style
	        computedStyle = el.style[prop];
	    }
	    
	    return computedStyle;
	
	}-*/;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy