Переглянути джерело

Merge pull request #333 from manolo/mcm_fixes

Allow to use customized animations. And some Fixes.
tags/gwtquery-project-1.4.3
Manuel Carrasco Moñino 9 роки тому
джерело
коміт
2e7592b13a
18 змінених файлів з 792 додано та 477 видалено
  1. 22
    0
      gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml
  2. 5
    7
      gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml
  3. 18
    16
      gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java
  4. 12
    3
      gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java
  5. 98
    26
      gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java
  6. 70
    61
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java
  7. 1
    1
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java
  8. 4
    23
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/ClipAnimation.java
  9. 0
    2
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Fx.java
  10. 10
    39
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java
  11. 237
    0
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transform.java
  12. 52
    222
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transitions.java
  13. 93
    51
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/TransitionsAnimation.java
  14. 16
    1
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java
  15. 1
    1
      gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/SpecialEvent.java
  16. 9
    3
      gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTestGwt.java
  17. 0
    2
      gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java
  18. 144
    19
      gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEffectsTestGwt.java

+ 22
- 0
gwtquery-core/src/main/java/com/google/gwt/query/Query.gwt.xml Переглянути файл

@@ -172,4 +172,26 @@
-->
<define-linker name="stddoctype" class="com.google.gwt.query.linker.IFrameWithDocTypeLinker"/>

<!-- Let the user select type of animations to use -->
<define-property name="animation" values="js,transition" />
<set-property name="animation" value="transition" />
<set-property name="animation" value="js">
<any>
<when-property-is name="user.agent" value="ie6" />
<when-property-is name="user.agent" value="ie8" />
<when-property-is name="user.agent" value="ie9" />
</any>
</set-property>
<!-- Animations based on JS loops -->
<replace-with class="com.google.gwt.query.client.plugins.effects.ClipAnimation">
<when-type-assignable
class="com.google.gwt.query.client.plugins.Effects.GQAnimation" />
<when-property-is name="animation" value="js" />
</replace-with>
<!-- Animations based on CSS3 transitions -->
<replace-with class="com.google.gwt.query.client.plugins.effects.TransitionsAnimation.TransitionsClipAnimation">
<when-type-assignable
class="com.google.gwt.query.client.plugins.Effects.GQAnimation" />
<when-property-is name="animation" value="transition" />
</replace-with>
</module>

+ 5
- 7
gwtquery-core/src/main/java/com/google/gwt/query/QueryMin.gwt.xml Переглянути файл

@@ -22,10 +22,10 @@

<!-- Inherit GQuery module -->
<inherits name='com.google.gwt.query.Query'/>
<source path="client"/>
<super-source path="super"/>
<super-source path="super"/>
<!-- Detect whether querySelectorAll is available -->
<define-property name="selectorCapability" values="native,js"/>
<property-provider name="selectorCapability">
@@ -39,11 +39,9 @@
<when-property-is name="user.agent" value="ie6" />
</set-property>
<set-property name="selectorCapability" value="native" >
<any>
<when-property-is name="user.agent" value="safari" />
</any>
<when-property-is name="user.agent" value="safari" />
</set-property>

<!-- Selector Engines -->
<replace-with class="com.google.gwt.query.client.impl.SelectorEngineNativeMin">
<when-type-assignable class="com.google.gwt.query.client.impl.SelectorEngineImpl"/>

+ 18
- 16
gwtquery-core/src/main/java/com/google/gwt/query/client/Function.java Переглянути файл

@@ -35,11 +35,11 @@ public abstract class Function {
* Utility method to get a string representation with the content
* of the arguments array. It recursively visits arrays and inspect
* object to print an appropriate representation of them.
*
*
* It is very useful to debug arguments passed in nested promises.
*
*
* It is protected so as it can be used in Inner functions.
*
*
* Output example:
* <pre>
* [0](com.google.gwt.query.client.plugins.QueuePlugin) <div>a1</div><div>a2</div>
@@ -124,7 +124,7 @@ public abstract class Function {

/**
* Return the first element of the arguments list.
*
*
* @deprecated use getArgument(idx) instead.
*/
@Deprecated
@@ -143,7 +143,7 @@ public abstract class Function {
/**
* Utility method for safety getting a JavaScriptObject present at a certain
* position in the list of arguments composed by arrays.
*
*
*/
@SuppressWarnings("unchecked")
public <T extends JavaScriptObject> T getArgumentJSO(int argIdx, int pos) {
@@ -161,10 +161,10 @@ public abstract class Function {
/**
* Utility method for safety getting an array present at a certain
* position in the list of arguments.
*
*
* Useful for Deferred chains where result of each resolved
* promise is set as an array in the arguments list.
*
*
* Always returns an array.
*/
public Object[] getArgumentArray(int idx) {
@@ -179,7 +179,7 @@ public abstract class Function {

/**
* Return the argument in the position idx or null if it doesn't exist.
*
*
* Note: if the return type doesn't match the object, you
* will get a casting exception.
*/
@@ -204,7 +204,7 @@ public abstract class Function {

/**
* Safety return the argument in the position idx.
*
*
* If the element class is not of the requested type it returns null and
* you don't get casting exeption.
*/
@@ -214,7 +214,7 @@ public abstract class Function {

/**
* Safety return the argument in the position idx.
*
*
* If the element class is not of the requested type it returns null and
* you don't get casting exeption.
*/
@@ -225,15 +225,15 @@ public abstract class Function {
/**
* Utility method for safety getting an object present at a certain
* position in the list of arguments composed by arrays.
*
*
* Useful for Deferred chains where result of each resolved
* promise is set as an array in the arguments list.
*
*
* When the object found in the array doesn't match the type required it returns a null.
*
*
* Note: If type is null, we don't check the class of the object found andd you could
* eventually get a casting exception.
*
*
*/
@SuppressWarnings("unchecked")
public <T> T getArgument(int argIdx, int pos, Class<? extends T> type) {
@@ -302,7 +302,7 @@ public abstract class Function {

/**
* Return the index in a loop execution.
*
*
* Used in GQuery.each()
*/
public int getIndex() {
@@ -427,6 +427,8 @@ public abstract class Function {
/**
* Override this method for bound event handlers if you wish to deal with
* per-handler user data.
*
* @return boolean false means stop propagation and prevent default
*/
public boolean f(Event e, Object... arg) {
setArguments(arg);
@@ -437,7 +439,7 @@ public abstract class Function {
/**
* Override this method for bound event handlers.
*
* @return boolean: false means stop propagation and prevent default
* @return boolean false means stop propagation and prevent default
*/
public boolean f(Event e) {
setEvent(e);

+ 12
- 3
gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsCache.java Переглянути файл

@@ -135,7 +135,10 @@ public class JsCache extends JavaScriptObject {
}

public final native boolean isEmpty() /*-{
for (k in this) return false;
for (k in this) {
if (this.hasOwnProperty(k))
return false;
}
return true;
}-*/;

@@ -185,7 +188,10 @@ public class JsCache extends JavaScriptObject {

var key, ret = 0;
// Chrome in DevMode injects a property to JS objects
for (key in this) if (key != "__gwt_ObjectId") ret ++;
for (key in this) {
if (key != "__gwt_ObjectId")
ret ++;
}
return ret;
}-*/;

@@ -240,7 +246,10 @@ public class JsCache extends JavaScriptObject {
var key, keys=[];
// Chrome in DevMode sets '__gwt_ObjectId' to JS objects
// GWT sets '$H' when calling getHashCode (see com/google/gwt/core/client/impl/Impl.java)
for(key in this) if (key != '__gwt_ObjectId' && key != '$H') keys.push(String(key));
for(key in this) {
if (this.hasOwnProperty(key) && key != '__gwt_ObjectId' && key != '$H')
keys.push(String(key));
}
return keys;
}-*/;


+ 98
- 26
gwtquery-core/src/main/java/com/google/gwt/query/client/js/JsUtils.java Переглянути файл

@@ -15,10 +15,13 @@
*/
package com.google.gwt.query.client.js;

import static com.google.gwt.query.client.GQuery.browser;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayMixed;
import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
@@ -90,9 +93,9 @@ public class JsUtils {
* Default JsUtils implementation.
*/
public static class JsUtilsImpl {
public native Properties parseJSON(String json) /*-{
return $wnd.JSON.parse(json);
}-*/;
public Properties parseJSON(String json) {
return JsonUtils.safeEval(json);
}

public native String JSON2String(JavaScriptObject o) /*-{
return $wnd.JSON.stringify(o);
@@ -129,15 +132,9 @@ public class JsUtils {
* IE JsUtils implemetation.
*/
public static class JsUtilsImplIE6 extends JsUtilsImpl {
public static final native Properties evalImpl(String properties) /*-{
return eval(properties);
}-*/;

@Override
public Properties parseJSON(String json) {
// No checks to the passed string so json should be
// a well-formed json string.
return evalImpl("(" + json + ")");
return JsonUtils.unsafeEval(json);
}

@Override
@@ -190,7 +187,7 @@ public class JsUtils {
@Override
public JsArray<Element> unique(JsArray<Element> a) {
// in IE6 XML elements does not support adding hashId to the object
if (a.length() > 0 && isXML(a.get(0))) {
if (browser.ie6 && isXML(a.get(0))) {
return a;
}
return super.unique(a);
@@ -299,11 +296,22 @@ public class JsUtils {
}

/**
* Check if an object has already a property with name <code>name</code>
* defined.
* Check if an object has a property with <code>name</code> defined.
* It supports dots in the name meaning checking nested properties.
*
* Example:
* <pre>
* // Check whether a browser supports touch events
* hasProperty(window, "ontouchstart");
* </pre>
*/
public static native boolean hasProperty(JavaScriptObject o, String name) /*-{
return o && name in o;
public static native boolean hasProperty(JavaScriptObject o, String name)/*-{
var p = name.split('.');
for (var i in p) {
if (!(o && p[i] in o)) return false;
o = o[p[i]];
}
return true;
}-*/;

/**
@@ -348,7 +356,7 @@ public class JsUtils {

/**
* Return whether a node is detached to the DOM.
*
*
* Be careful : This method works only on node that should be inserted within the body node.
*/
public static boolean isDetached(Node n) {
@@ -480,6 +488,63 @@ public class JsUtils {
return a
}-*/;

/**
* Call any arbitrary function present in a Javascript object.
* It checks the existence of the function and object hierarchy before executing it.
* It's very useful in order to avoid writing jsni blocks for very simple snippets.
*
* Note that GWT 3.0 jsinterop will come with a method similar, so we might deprecate
* this in the future.
*
* Example
* <pre>
* // Create a svg node in our document.
* Element svg = jsni(document, "createElementNS", "http://www.w3.org/2000/svg", "svg");
* // Append it to the dom
* $(svg).appendTo(document);
* // show the svg element in the debug console
* jsni("console.log", svg);
* </pre>
*
* @param jso the object containing the method to execute
* @param meth the literal name of the function to call, dot separators are allowed.
* @param args an array with the arguments to pass to the function.
* @return the java ready boxed object returned by the jsni method or null, if the
* call return a number we will get a Double, if it returns a boolean we get a java
* Boolean, strings comes as java String, otherwise we get the javascript object.
*/
public static <T> T jsni(JavaScriptObject jso, String meth, Object... args) {
return runJavascriptFunction(jso, meth, args);
}

/**
* Run any arbitrary function in javascript scope using the window as the base object.
* It checks the existence of the function and object hierarchy before executing it.
* It's very useful in order to avoid writing jsni blocks for very simple snippets.
*
* Note that GWT 3.0 jsinterop will come with a method similar, so we might deprecate
* this in the future.
*
* Example
* <pre>
* // Create a svg node in our document.
* Element svg = jsni("document.createElementNS", "http://www.w3.org/2000/svg", "svg");
* // Append it to the dom
* $(svg).appendTo(document);
* // show the svg element in the debug console
* jsni("console.log", svg);
* </pre>
*
* @param meth the literal name of the function to call, dot separators are allowed.
* @param args an array with the arguments to pass to the function.
* @return the java ready boxed object returned by the jsni method or null, if the
* call return a number we will get a Double, if it returns a boolean we get a java
* Boolean, strings comes as java String, otherwise we get the javascript object.
*/
public static <T> T jsni(String meth, Object... args) {
return runJavascriptFunction(null, meth, args);
}

/**
* Call via jsni any arbitrary function present in a Javascript object.
*
@@ -494,20 +559,27 @@ public class JsUtils {
* $(svg).appendTo(document);
* </pre>
*
* @param o the javascript object where the function is.
* @param meth the literal name of the function to call.
* @param o the javascript object where the function is, it it is null we use window.
* @param meth the literal name of the function to call, dot separators are allowed.
* @param args an array with the arguments to pass to the function.
* @return the javascript object returned by the jsni method or null.
* @return the java ready boxed object returned by the jsni method or null, if the
* call return a number we will get a Double, if it returns a boolean we get a java
* Boolean, strings comes as java String, otherwise we get the javascript object.
*
* @deprecated use jsni instead.
*/
public static <T> T runJavascriptFunction(JavaScriptObject o, String meth, Object... args) {
return runJavascriptFunctionImpl(o, meth, JsObjectArray.create().add(args)
.<JsArrayMixed> cast());
return runJavascriptFunctionImpl(o, meth, JsObjectArray.create().add(args).<JsArrayMixed>cast());
}

private static native <T> T runJavascriptFunctionImpl(JavaScriptObject o, String meth,
JsArrayMixed args) /*-{
return (f = o && o[meth])
&& @com.google.gwt.query.client.js.JsUtils::isFunction(*)(f)
private static native <T> T runJavascriptFunctionImpl(JavaScriptObject o, String meth, JsArrayMixed args) /*-{
var f = o || window, p = meth.split('.');
for (var i in p) {
o = f;
f = f[p[i]];
if (!f) return null;
}
return @com.google.gwt.query.client.js.JsUtils::isFunction(*)(f)
&& @com.google.gwt.query.client.js.JsCache::gwtBox(*)([f.apply(o, args)]);
}-*/;

@@ -542,7 +614,7 @@ public class JsUtils {

/**
* Returns a QueryString representation of a JavascriptObject.
*
*
* TODO: jquery implementation accepts a second parameter (traditional)
*/
public static String param(JavaScriptObject js) {

+ 70
- 61
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Effects.java Переглянути файл

@@ -16,16 +16,15 @@
package com.google.gwt.query.client.plugins;

import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.plugins.effects.ClipAnimation;
import com.google.gwt.query.client.plugins.effects.ClipAnimation.Direction;
import com.google.gwt.query.client.js.JsMap;
import com.google.gwt.query.client.plugins.effects.Fx;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.Easing;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.EasingCurve;
import com.google.gwt.query.client.plugins.effects.TransitionsAnimation.TransitionsClipAnimation;

/**
* Effects plugin for Gwt Query.
@@ -42,14 +41,34 @@ public class Effects extends QueuePlugin<Effects> {
// Each Animation is associated to one element
protected Element e;
protected Properties prps;
protected Easing easing;
protected Function[] funcs;
protected Effects g;

protected GQAnimation setElement(Element element) {
public GQAnimation setElement(Element element) {
e = element;
g = $(e).as(Effects);
return this;
}

protected GQAnimation setProperties(Properties properties) {
public GQAnimation setProperties(Properties properties) {
prps = properties == null ? Properties.create() : properties;
if (prps.defined("easing")) {
try {
easing = EasingCurve.valueOf(prps.getStr("easing"));
} catch (Exception ignore) {
}
}
return this;
}

public GQAnimation setEasing(Easing ease) {
easing = ease != null ? ease : EasingCurve.swing;
return this;
}

public GQAnimation setCallback(Function... f) {
funcs = f;
return this;
}

@@ -92,12 +111,20 @@ public class Effects extends QueuePlugin<Effects> {
super(gq);
}

private void queueAnimation(final Element e, final GQAnimation anim, final int duration) {
/**
* Queue an animation for an element.
*
* The goal of this method is to reuse animations.
* @param e
* @param anim
* @param duration
*/
public void queueAnimation(final GQAnimation anim, final int duration) {
if (isOff()) {
anim.onStart();
anim.onComplete();
} else {
queue(e, DEFAULT_NAME, new Function() {
queue(anim.e, DEFAULT_NAME, new Function() {
public void cancel(Element e) {
Animation anim = (Animation) data(e, GQAnimation.ACTUAL_ANIMATION, null);
if (anim != null) {
@@ -116,6 +143,30 @@ public class Effects extends QueuePlugin<Effects> {
return Fx.off;
}

/**
* Maintain a cache table with vendor properties so as plugins can use it.
*/
public static JsMap<String, String>vendorPropNames;

/**
* Browser prefix for vendor spedific properties.
*/
public static String prefix;

static {
if (GWT.isClient()) {
vendorPropNames = JsMap.create();
prefix = browser.msie ? "ms" : browser.opera ? "o" : browser.mozilla ? "moz" : browser.webkit ? "webkit" : "";
}
}

/**
* Get the cached vendor property name.
*/
public static String vendorProperty(String prop) {
return vendorPropNames.get(prop) != null ? vendorPropNames.get(prop) : prop;
}

/**
* The animate() method allows you to create animation effects on any numeric
* Attribute, CSS property, or color CSS property.
@@ -169,7 +220,6 @@ public class Effects extends QueuePlugin<Effects> {
* @param easing the easing function to use for the transition
*/
public Effects animate(Object stringOrProperties, int duration, Easing easing, Function... funcs) {

final Properties p = (stringOrProperties instanceof String)
? (Properties) $$((String) stringOrProperties)
: (Properties) stringOrProperties;
@@ -177,19 +227,25 @@ public class Effects extends QueuePlugin<Effects> {
if (p.getStr("duration") != null) {
duration = p.getInt("duration");
}

duration = Math.abs(duration);

for (Element e : elements()) {
if (Fx.css3) {
new TransitionsClipAnimation(easing, e, p, funcs).run(duration);
} else {
queueAnimation(e, new ClipAnimation(easing, e, p, funcs), duration);
}
GQAnimation a = createAnimation();
a.setEasing(easing).setProperties(p).setElement(e).setCallback(funcs);
queueAnimation(a, duration);
}
return this;
}

/**
* Override this to create plugins with customized animation implementation.
*
* By default it uses deferred binding.
*/
protected GQAnimation createAnimation() {
return GWT.create(GQAnimation.class);
}

/**
*
* The animate() method allows you to create animation effects on any numeric
@@ -308,53 +364,6 @@ public class Effects extends QueuePlugin<Effects> {
return animate(stringOrProperties, duration, EasingCurve.linear, funcs);
}

/**
* Animate the set of matched elements using the clip or scale property. It is possible to show or
* hide a set of elements, specify the direction of the animation and the start corner of the
* effect. Finally it executes the set of functions passed as arguments.
*
* @deprecated use animate() instead
*/
@Deprecated
public Effects clip(ClipAnimation.Action a, ClipAnimation.Corner c,
ClipAnimation.Direction d, Function... f) {
return clip(a, c, d, Speed.DEFAULT, f);
}

/**
* Animate the set of matched elements using the clip or scale property. It is possible to show or
* hide a set of elements, specify the direction of the animation and the start corner of the
* effect. Finally it executes the set of functions passed as arguments.
*
* @deprecated use animate() instead
*/
@Deprecated
public Effects clip(final ClipAnimation.Action a,
final ClipAnimation.Corner c, final ClipAnimation.Direction d,
final int duration, final Function... f) {
for (Element e : elements()) {
if (Fx.css3) {
new TransitionsClipAnimation(e, a, c, d, null, null, f).run(duration);
} else {
queueAnimation(e, new ClipAnimation(e, a, c, d, f), duration);
}
}
return this;
}

/**
* Animate the set of matched elements using the clip or scale property. It is possible to show or
* hide a set of elements, specify the direction of the animation and the start corner of the
* effect. Finally it executes the set of functions passed as arguments.
*
* @deprecated use animate() instead
*/
@Deprecated
public Effects clip(ClipAnimation.Action a, ClipAnimation.Corner c,
Function... f) {
return clip(a, c, Direction.BIDIRECTIONAL, Speed.DEFAULT, f);
}

/**
* Reveal all matched elements by adjusting the clip or scale property firing an optional callback
* after completion. The effect goes from the center to the perimeter.

+ 1
- 1
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/Events.java Переглянути файл

@@ -394,7 +394,7 @@ public class Events extends GQuery {
dispatchEvent(evt, null, funcs);
}

private void dispatchEvent(NativeEvent evt, Object[] datas, Function... funcs) {
public void dispatchEvent(NativeEvent evt, Object[] datas, Function... funcs) {
for (Element e : elements()) {
if (isEventCapable(e)) {
$(e).data(EventsListener.EVENT_DATA, datas);

+ 4
- 23
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/ClipAnimation.java Переглянути файл

@@ -15,12 +15,11 @@
*/
package com.google.gwt.query.client.plugins.effects;

import com.google.gwt.dom.client.Element;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.js.JsUtils;
import com.google.gwt.query.client.plugins.Effects;
import com.google.gwt.query.client.plugins.Effects.GQAnimation;

/**
* Animation which uses the css clip property to show/hide an element.
@@ -55,15 +54,10 @@ public class ClipAnimation extends PropertiesAnimation {
private Corner corner;
private Direction direction;
private GQuery back = Effects.$();
private Effects g;
private Action currentAction;

public ClipAnimation(Element elem, Properties p, Function... funcs) {
this(null, elem, p, funcs);
}

public ClipAnimation(Easing easing, Element elem, Properties p, Function... funcs) {
super(easing, elem, p, funcs);
@Override
public GQAnimation setProperties(Properties p) {
corner = Corner.CENTER;
try {
corner = Corner.valueOf(getNormalizedValue("clip-origin", p));
@@ -78,26 +72,13 @@ public class ClipAnimation extends PropertiesAnimation {
action = Action.valueOf(getNormalizedValue("clip-action", p));
} catch (Exception e) {
}
g = GQuery.$(e).as(Effects.Effects);
return super.setProperties(p);
}

public static String getNormalizedValue(String value, Properties p) {
return JsUtils.hyphenize(p.getStr(value)).replace("-", "_").toUpperCase();
}

public ClipAnimation(Element elem, Action a, Corner c, Direction d, Easing easing,
Properties p, final Function... funcs) {
super(easing, elem, p, funcs);
this.action = a;
this.corner = c;
this.direction = d;
g = GQuery.$(e).as(Effects.Effects);
}

public ClipAnimation(Element elem, Action a, Corner c, Direction d, final Function... funcs) {
this(elem, a, c, d, null, Properties.create(), funcs);
}

@Override
public void onComplete() {
super.onComplete();

+ 0
- 2
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Fx.java Переглянути файл

@@ -33,8 +33,6 @@ public class Fx {
*/
public static boolean off = false;

public static boolean css3 = Transitions.transition != null;

/**
* A pojo to store css3 transition values.
*/

+ 10
- 39
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/PropertiesAnimation.java Переглянути файл

@@ -15,12 +15,8 @@
*/
package com.google.gwt.query.client.plugins.effects;

import static com.google.gwt.query.client.GQuery.$;

import com.google.gwt.dom.client.Element;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.js.JsObjectArray;
import com.google.gwt.query.client.plugins.Effects;
import com.google.gwt.query.client.plugins.Effects.GQAnimation;
@@ -153,13 +149,17 @@ public class PropertiesAnimation extends GQAnimation {

protected static final String[] ATTRS_TO_SAVE = new String[] {"overflow"};

private static final RegExp REGEX_NUMBER_UNIT = RegExp.compile("^([0-9+-.]+)(.*)?$");
protected static final String NUMBER = "[\\d+-.]+";
protected static final String UNIT = "[a-z%]+";

private static final RegExp REGEX_NUMBER_UNIT = RegExp.compile("^(" + NUMBER + ")(.*)?$");

protected static final RegExp REGEX_SYMBOL_NUMBER_UNIT = RegExp
.compile("^([+-]=)?([0-9+-.]+)(.*)?$");
protected static final RegExp REGEX_SYMBOL_NUMBER_UNIT = RegExp.compile("^([+-]=)?(" + NUMBER + ")(" + UNIT + ")?$");

protected static final RegExp REGEX_NON_PIXEL_ATTRS =
RegExp.compile("z-?index|font-?weight|opacity|zoom|line-?height|scale|rotation|^\\$", "i");
protected static final RegExp REGEX_SCALE_ATTRS = RegExp.compile("scale|opacity");

protected static final RegExp REGEX_NON_PIXEL_ATTRS = RegExp.compile("scale|opacity"
+ "|z-?index|font-?weight|zoom|line-?height|rotat|skew|perspect|^\\$", "i");

private static final RegExp REGEX_COLOR_ATTR = RegExp.compile(".*color$", "i");

@@ -173,7 +173,6 @@ public class PropertiesAnimation extends GQAnimation {
if (REGEX_COLOR_ATTR.test(key)) {
return computeFxColorProp(e, key, val);
}

return computeFxNumericProp(e, key, val, hidden);
}

@@ -251,9 +250,6 @@ public class PropertiesAnimation extends GQAnimation {
start = 0;
unit = REGEX_NON_PIXEL_ATTRS.test(key) ? "" : "px";
} else if ("hide".equals(val)) {
if (hidden) {
return null;
}
g.saveCssAttrs(key);
end = 0;
unit = REGEX_NON_PIXEL_ATTRS.test(key) ? "" : "px";
@@ -288,32 +284,7 @@ public class PropertiesAnimation extends GQAnimation {
return new Fx(key, val, start, end, unit, rkey);
}

protected Easing easing;
protected JsObjectArray<Fx> effects;
private Function[] funcs;
private Effects g;

public PropertiesAnimation(Element elem, Properties p, Function... funcs) {
this(null, elem, p, funcs);
}

public PropertiesAnimation(Easing ease, Element elem, Properties p, Function... funcs) {
try {
easing = EasingCurve.valueOf(p.getStr("easing"));
} catch (Exception e) {
}
if (easing == null) {
easing = ease;
}
if (easing == null) {
easing = EasingCurve.swing;
}
this.funcs = funcs;
setProperties(p);
setElement(elem);

g = $(e).as(Effects.Effects);
}

@Override
public void onCancel() {
@@ -329,7 +300,7 @@ public class PropertiesAnimation extends GQAnimation {
@Override
public void onComplete() {
super.onComplete();
for (int i = 0; i < effects.length(); i++) {
for (int i = 0; effects != null && i < effects.length(); i++) {
Fx fx = effects.get(i);
if ("hide".equals(fx.value)) {
g.hide();

+ 237
- 0
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transform.java Переглянути файл

@@ -0,0 +1,237 @@
/*
* 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 = vendorProperty("transform");
public static final String transformOrigin = 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<String, List<String>>();

// Some browsers like HTMLUnit only support 2d transformations
private static boolean supportsTransform3d() {
if (transform == null) {
return false;
}
String rotate = "rotateY(1deg)";
divStyle.setProperty(transform, rotate);
rotate = divStyle.getProperty(transform);
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, 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));
}
}

+ 52
- 222
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/Transitions.java Переглянути файл

@@ -28,26 +28,25 @@
*/
package com.google.gwt.query.client.plugins.effects;

import com.google.gwt.core.client.Duration;
import static com.google.gwt.query.client.plugins.effects.Transform.getInstance;
import static com.google.gwt.query.client.plugins.effects.Transform.isTransform;
import static com.google.gwt.query.client.plugins.effects.Transform.transform;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.js.JsUtils;
import com.google.gwt.query.client.plugins.Effects;
import com.google.gwt.query.client.plugins.Plugin;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.Easing;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.EasingCurve;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.query.client.plugins.effects.TransitionsAnimation.TransitionsClipAnimation;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

/**
* Transitions and transformation plugin for gQuery.
@@ -69,127 +68,14 @@ import java.util.Map.Entry;

* </pre>
*/
public class Transitions extends GQuery {

/**
* A dictionary class with all the properties of an element transform
* which is able to return the correct syntax for setting css properties.
*/
public static class Transform {

private static final RegExp transform3dRegex = RegExp.compile("^(rotate([XY]|3d)|perspective)$");

private HashMap<String, List<String>> map = new HashMap<String, List<String>>();

public Transform(String s) {
parse(s);
}

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;
}

private void parse(String s) {
if (s != null) {
RegExp re = RegExp.compile("([a-zA-Z0-9]+)\\((.*?)\\)", "g");
for (MatchResult r = re.exec(s); r != null; r = re.exec(s)) {
setFromString(r.getGroup(1), r.getGroup(2));
}
}
}

public void set(String prop, String ...val) {
setter(prop, val);
}

public void setFromString(String prop, String ...val) {
if (val.length == 1) {
String[] vals = val[0].split("[\\s*,\\s*]");
set(prop, vals);
} else {
set(prop, val);
}
}

private void setter(String prop, String ...val) {
if (prop.matches("(rotate[XY]?|skew[XY])")) {
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 ("x".equals(prop)) {
setter("translate", val[0], null);
} else if ("y".equals(prop)) {
setter("translate", null, val[0]);
} else if ("translate".equals(prop)) {
if (map.get("translateX") == null) {
map.put("translateX", unit("0", "px"));
}
if (val[0] != null) {
map.put("translateX", unit(val[0], "px"));
}
if (map.get("translateY") == null) {
map.put("translateY", unit("0", "px"));
}
if (val[1] != null) {
map.put("translateY", unit(val[1], "px"));
}
map.put("translate", Arrays.asList(map.get("translateX").get(0), map.get("translateY").get(0)));
}
}

/**
* Converts the dictionary to a transition css string.
*/
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));
}
}

// Used to check supported properties in the browser
private static Style divStyle = DOM.createDiv().getStyle();

private static final String prefix = browser.msie ? "ms" : browser.opera ? "o" : browser.mozilla ? "moz" : browser.webkit ? "webkit" : "";
private static final String transform = getVendorPropertyName("transform");
private static final String TRANSFORM = "_t_";
private static final String transformOrigin = getVendorPropertyName("transformOrigin");

protected static final RegExp transformRegex = RegExp.compile("^(scale|translate|rotate([XY]|3d)?|perspective|skew[XY]|x|y)$");
protected static final String transition = getVendorPropertyName("transition");
public class Transitions extends Effects {

// passing an invalid transition property in chrome, makes disable all transitions in the element
private static final RegExp invalidTransitionNamesRegex = RegExp.compile("^(.*transform.*|duration|easing|delay|clip-.*)$");
public static final RegExp invalidTransitionNamesRegex = RegExp.compile("^(.*transform.*|duration|function|easing|delay|clip-.*)$");

private static final String transitionDelay = getVendorPropertyName("transitionDelay");
private static final String transitionEnd = browser.mozilla || browser.msie ? "transitionend" : (prefix + "transitionEnd");
protected static final String transitionEnd = browser.mozilla || browser.msie ? "transitionend" : (prefix + "TransitionEnd");

public static boolean has3d = supportsTransform3d();
protected static final String transition = vendorProperty("transition");

public static final Class<Transitions> Transitions = GQuery.registerPlugin(
Transitions.class, new Plugin<Transitions>() {
@@ -198,83 +84,50 @@ public class Transitions extends GQuery {
}
});

private 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;
}

private static String property(String prop) {
if (transformRegex.test(prop)) {
if (isTransform(prop)) {
return transform;
}
return prop.replaceFirst("^(margin|padding).+$", "$1");
}

private static boolean supportsTransform3d() {
if (transform == null) {
return false;
}
String rotate = "rotateY(1deg)";
divStyle.setProperty(transform, rotate);
rotate = divStyle.getProperty(transform);
return rotate != null && !rotate.isEmpty();
}

protected Transitions(GQuery gq) {
super(gq);
}

@Override
public String css(String prop, boolean force) {
if ("transform".equals(prop)) {
return isEmpty() ? "" : getTransform(get(0), null).toString();
} else if ("transformOrigin".equals(prop)) {
return super.css(transformOrigin, force);
} else if ("transition".equals(prop)) {
return super.css(transition, force);
} else if (transformRegex.test(prop)) {
return isEmpty() ? "" : getTransform(get(0), null).get(prop);
prop = vendorProperty(prop);
if (transform.equals(prop)) {
return isEmpty() ? "" : getInstance(get(0), null).toString();
} else if (isTransform(prop)) {
return isEmpty() ? "" : getInstance(get(0), null).get(prop);
} else {
String ret = super.css(prop, force);
return ret;
return super.css(prop, force);
}
}

@Override
public Transitions css(String prop, String value) {
if ("transform".equals(prop)) {
prop = vendorProperty(prop);
if (transform.equals(prop)) {
for (Element e : elements()) {
Transform t = getTransform(e, value);
Transform t = getInstance(e, value != null ? value : "");
getStyleImpl().setStyleProperty(e, transform, t.toString());
}
} else if ("transformOrigin".equals(prop)) {
super.css(transformOrigin, value);
} else if ("transition".equals(prop)) {
super.css(transition, value);
} else if (transformRegex.test(prop)) {
} else if (isTransform(prop)) {
for (Element e : elements()) {
Transform t = getTransform(e, null);
Transform t = getInstance(e, null);
t.setFromString(prop, value);
getStyleImpl().setStyleProperty(e, transform, t.toString());
}
} else if (!invalidTransitionNamesRegex.test(prop)) {
} else {
super.css(prop, value);
}
return this;
}

private List<String> filterTransitionPropertyNames(Properties p) {
public static List<String> filterTransitionPropertyNames(Properties p) {
List<String> ret = new ArrayList<String>();
for (String s : p.keys()) {
if (invalidTransitionNamesRegex.test(s)) {
@@ -295,15 +148,6 @@ public class Transitions extends GQuery {
return ret;
}

private Transform getTransform(Element e, String initial) {
Transform t = data(e, TRANSFORM);
if (t == null || initial != null && !initial.isEmpty()) {
t = new Transform(initial);
data(e, TRANSFORM, t);
}
return t;
}

/**
* The transition() method allows you to create animation effects on any numeric HTML Attribute,
* CSS property, or color using CSS3 transformations and transitions.
@@ -320,49 +164,17 @@ public class Transitions extends GQuery {
.transition("{x: +100, width: +40px}", 2000, EasingCurve.easeOut);
* </pre>
*/
public Transitions transition(Object stringOrProperties, final int duration, final Easing easing, final int delay, final Function... funcs) {
if (isEmpty()) {
return this;
}

final Properties cssProps = (stringOrProperties instanceof String)
? (Properties) $$((String) stringOrProperties)
: (Properties) stringOrProperties;

final String ease = easing == null ? "ease" : easing.toString();
final List<String> transProps = filterTransitionPropertyNames(cssProps);
final double queuedAt = delay > 0 ? Duration.currentTimeMillis() : 0;

// Use gQuery queue, so as we can chain transitions, animations etc.
queue(new Function() {
public void f() {
// This is called once per element
final String oldTransitionValue = $(this).css(transition);
// Recompute delay based on the time spent in the queue
int d = Math.max(0, delay - (int) (Duration.currentTimeMillis() - queuedAt));
// Generate transition value
String attribs = duration + "ms" + " " + ease + " " + d + "ms";
String newTransitionValue = "";
for (String s : transProps) {
newTransitionValue += (newTransitionValue.isEmpty() ? "" : ", ") + s + " " + attribs;
}

final Transitions thisTrans = $(this).as(Transitions);
// Configure animation using transition property
thisTrans.css(transition, newTransitionValue);
// Set all css properties for this transition using the css method in this class
thisTrans.css(cssProps);

// TODO: Use transitionEnd events once GQuery supports non-bit events
// last time I tried, setting 'transitionEnd' made custom events fail (slideEnter)
new Timer() {
public void run() {
thisTrans.css(transition, oldTransitionValue).each(funcs).dequeue();
}
}.schedule(d + duration);
public Transitions transition(Object stringOrProperties, final int duration, final Easing easing,
final int delay, final Function... funcs) {
if (!isEmpty()) {
Properties p = (stringOrProperties instanceof String)
? $$((String) stringOrProperties)
: (Properties) stringOrProperties;
for (Element e : elements()) {
queueAnimation(new TransitionsClipAnimation().setEasing(easing).setProperties(p)
.setElement(e).setCallback(funcs), duration);
}
});

}
return this;
}

@@ -380,4 +192,22 @@ public class Transitions extends GQuery {
public Transitions transition(Object stringOrProperties, int duration, String easing, int delay) {
return transition(stringOrProperties, duration, EasingCurve.valueOf(easing), delay);
}

@Override
protected GQAnimation createAnimation() {
return new TransitionsAnimation();
}

@Override
public boolean isVisible() {
for (String s : Arrays.asList("opacity", "scale", "scaleX", "scaleY", "scale3d", "width", "height")) {
String[] parts = css(s).split("\\s*,\\s*");
for (String p : parts) {
if (p.matches("^0[a-z%]*")) {
return false;
}
}
}
return super.isVisible();
}
}

+ 93
- 51
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/effects/TransitionsAnimation.java Переглянути файл

@@ -20,13 +20,16 @@ import static com.google.gwt.query.client.GQuery.$$;
import static com.google.gwt.query.client.plugins.effects.ClipAnimation.getNormalizedValue;

import com.google.gwt.dom.client.Element;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.plugins.Effects.GQAnimation;
import com.google.gwt.query.client.plugins.effects.ClipAnimation.Action;
import com.google.gwt.query.client.plugins.effects.ClipAnimation.Corner;
import com.google.gwt.query.client.plugins.effects.ClipAnimation.Direction;
import com.google.gwt.query.client.plugins.effects.Fx.TransitFx;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.user.client.Timer;

import java.util.List;

/**
* Animation effects on any numeric CSS3 property or transformation
@@ -44,12 +47,8 @@ public class TransitionsAnimation extends PropertiesAnimation {
private Direction direction;
private Action currentAction;

public TransitionsClipAnimation(Element elem, Properties p, Function... funcs) {
this(null, elem, p, funcs);
}

public TransitionsClipAnimation(Easing easing, Element elem, Properties p, Function... funcs) {
super(easing, elem, p, funcs);
@Override
public GQAnimation setProperties(Properties p) {
corner = Corner.CENTER;
try {
corner = Corner.valueOf(getNormalizedValue("clip-origin", p));
@@ -64,18 +63,11 @@ public class TransitionsAnimation extends PropertiesAnimation {
action = Action.valueOf(getNormalizedValue("clip-action", p));
} catch (Exception e) {
}
}

public TransitionsClipAnimation(Element elem, Action a, Corner c, Direction d, Easing easing,
Properties p, final Function... funcs) {
super(easing, elem, p, funcs);
this.action = a;
this.corner = c;
this.direction = d;
return super.setProperties(p);
}

public void onStart() {
boolean hidden = !g.isVisible();
boolean hidden = !t.isVisible();

super.onStart();
if (action == null) {
@@ -104,7 +96,7 @@ public class TransitionsAnimation extends PropertiesAnimation {
originY = "bottom";
}

g.show().css("transformOrigin", originX + " " + originY);
t.show().css("transformOrigin", originX + " " + originY);

effects.add(new TransitFx("scale", "", scaleXini + " " + scaleYini, scaleXend + " " + scaleYend, ""));
}
@@ -116,16 +108,16 @@ public class TransitionsAnimation extends PropertiesAnimation {
return;
}
if (currentAction == Action.HIDE) {
g.hide();
t.hide();
}
g.css("transformOrigin", "");
g.css("transform", "scale(1 1)");
t.css("transformOrigin", "");
t.css("transform", "");
}
}

public static Fx computeFxProp(Element e, String key, String val, boolean hidden) {
public static TransitFx computeFxProp(Element e, String key, String val, boolean hidden) {
Transitions g = $(e).as(Transitions.Transitions);
String unit = "";
String unit = REGEX_NON_PIXEL_ATTRS.test(key) ? "" : "px";
if ("toggle".equals(val)) {
val = hidden ? "show" : "hide";
}
@@ -139,13 +131,21 @@ public class TransitionsAnimation extends PropertiesAnimation {
}

String cur = g.css(key, true);
String trsStart = cur, trsEnd = trsStart;
String trsStart = cur.matches("auto|initial") ? "" : cur, trsEnd = trsStart;

if ("show".equals(val)) {
g.saveCssAttrs(key);
trsStart = "0";
if (trsStart.isEmpty()) {
trsStart = "0";
}
if (REGEX_SCALE_ATTRS.test(key)) {
trsEnd = "1";
}
} else if ("hide".equals(val)) {
g.saveCssAttrs(key);
if (trsStart.isEmpty() && REGEX_SCALE_ATTRS.test(key)) {
trsStart = "1";
}
trsEnd = "0";
} else {
MatchResult parts = REGEX_SYMBOL_NUMBER_UNIT.exec(val);
@@ -156,15 +156,16 @@ public class TransitionsAnimation extends PropertiesAnimation {
String part3 = parts.getGroup(3);
trsEnd = "" + Double.parseDouble(part2);

unit = REGEX_NON_PIXEL_ATTRS.test(key) ? "" : part3 == null || part3.isEmpty() ? "px" : part3;
if (part3 != null && !part3.isEmpty()) {
unit = part3;
}

if (trsStart.isEmpty()) {
trsStart = key.matches("scale") ? "1" : "0";
trsStart = REGEX_SCALE_ATTRS.test(key) ? "1" : "0";
}

if (part1 != null && !part1.isEmpty()) {
double n = "-=".equals(part1) ? -1 : 1;

double st = 0;
MatchResult sparts = REGEX_SYMBOL_NUMBER_UNIT.exec(trsStart);
if (sparts != null) {
@@ -172,48 +173,67 @@ public class TransitionsAnimation extends PropertiesAnimation {
unit = sparts.getGroup(3) == null || sparts.getGroup(3).isEmpty() ? unit : sparts.getGroup(3);
}
trsStart = "" + st;

double en = Double.parseDouble(trsEnd);
trsEnd = "" + (st + n * en);
}

// Deal with non px units like "%"
if (!unit.isEmpty() && !"px".equals(unit) && trsStart.matches("[-+]?[\\d.]+")) {
if (!unit.isEmpty() && !"px".equals(unit) && trsStart.matches(NUMBER)) {
double start = Double.parseDouble(trsStart);
double to = Double.parseDouble(trsEnd);
g.css(key, to + unit);
start = to * start / g.cur(key, true);
trsStart = "" + start;
g.css(key, start + unit);
if (start != 0) {
double to = Double.parseDouble(trsEnd);
g.css(key, to + unit);
start = to * start / g.cur(key, true);
trsStart = "" + start;
g.css(key, start + unit);
}
}
} else {
trsStart = "";
trsEnd = val;
if (trsStart.isEmpty()) {
trsStart = REGEX_SCALE_ATTRS.test(key) ? "1" : "0";
}
}
}
if (trsStart.matches(NUMBER)) {
trsStart += unit;
}
if (trsEnd.matches(NUMBER)) {
trsEnd += unit;
}
return new TransitFx(key, val, trsStart, trsEnd, unit);
}

protected Transitions g;
protected Transitions t;
protected int delay = 0;
private String oldTransitionValue;

public TransitionsAnimation(Element elem, Properties p, Function... funcs) {
this(null, elem, p, funcs);
@Override
public GQAnimation setProperties(Properties p) {
delay = p.getInt("delay");
return super.setProperties(p);
}

public TransitionsAnimation(Easing easing, Element elem, Properties p, Function... funcs) {
super(easing, elem, p, funcs);
delay = p.getInt("delay");
g = $(e).as(Transitions.Transitions);
@Override
public GQAnimation setElement(Element elem) {
e = elem;
g = t = $(elem).as(Transitions.Transitions);
return this;
}

public TransitionsAnimation setDelay(int delay) {
this.delay = delay;
return this;
}

private Properties getFxProperties(boolean isStart) {
public Properties getFxProperties(boolean isStart) {
Properties p = $$();
for (int i = 0; i < effects.length(); i++) {
TransitFx fx = (TransitFx) effects.get(i);
String val = isStart ? fx.transitStart : fx.transitEnd;
if (!val.isEmpty()) {
p.set(fx.cssprop, val + fx.unit);
p.set(fx.cssprop, val);
}
}
return p;
@@ -221,7 +241,7 @@ public class TransitionsAnimation extends PropertiesAnimation {

@Override
protected Fx getFx(Element e, String key, String val, boolean hidden) {
return computeFxProp(e, key, val, hidden);
return Transitions.invalidTransitionNamesRegex.test(key) ? null : computeFxProp(e, key, val, hidden);
}

@Override
@@ -229,21 +249,43 @@ public class TransitionsAnimation extends PropertiesAnimation {
}

@Override
public void onComplete() {
t.css(Transitions.transition, oldTransitionValue);
super.onComplete();
}

public void run(int duration) {
// Calculate all Fx values for this animation
onStart();

// Compute initial properties
Properties p = getFxProperties(true);
g.css(p)
// Some browsers need after setting initial properties re-flow (FF 24.4.0).
.offset();
t.css(p);
// Some browsers need re-flow after setting initial properties (FF 24.4.0).
t.offset();

// Compute final properties
p = getFxProperties(false);
g.transition(p, duration, easing, delay, new Function() {
public void f() {

// Save old transition value
oldTransitionValue = t.css(Transitions.transition);

// Set new transition value
String newTransitionValue = "";
List<String> transProps = Transitions.filterTransitionPropertyNames(p);
String attribs = duration + "ms" + " " + easing + " " + delay + "ms";
for (String s : transProps) {
newTransitionValue += (newTransitionValue.isEmpty() ? "" : ", ") + s + " " + attribs;
}
t.css(Transitions.transition, newTransitionValue);

// Set new css properties so as the element is animated
t.css(p);

// Wait until transition has finished to run finish animation and dequeue
new Timer() {
public void run() {
onComplete();
}
});
}.schedule(delay + duration);
}
}

+ 16
- 1
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/EventsListener.java Переглянути файл

@@ -14,6 +14,7 @@
package com.google.gwt.query.client.plugins.events;

import static com.google.gwt.query.client.GQuery.$;
import static com.google.gwt.query.client.GQuery.console;

import com.google.gwt.core.client.Duration;
import com.google.gwt.dom.client.Element;
@@ -641,6 +642,11 @@ public class EventsListener implements EventListener {
}

public void onBrowserEvent(Event event) {
// console.log("onBrowser", event.getType(), event, element);
if (JsUtils.isDefaultPrevented(event)) {
console.log("RETTT");
return;
}
double now = Duration.currentTimeMillis();
// Workaround for Issue_20
if (lastType.equals(event.getType()) && now - lastEvnt < 10
@@ -688,9 +694,18 @@ public class EventsListener implements EventListener {
* given eventBit or eventName.
*/
public boolean hasHandlers(int eventBits, String eventName) {
return hasHandlers(eventBits, eventName, null);
}

/**
* Return true if the element is listening for the
* given eventBit or eventName and the handler matches.
*/
public boolean hasHandlers(int eventBits, String eventName, Function handler) {
for (int i = 0, j = elementEvents.length(); i < j; i++) {
BindFunction function = elementEvents.get(i);
if (function.hasEventType(eventBits) || function.isTypeOf(eventName)) {
if ((function.hasEventType(eventBits) || function.isTypeOf(eventName))
&& (handler == null || function.isEquals(handler))) {
return true;
}
}

+ 1
- 1
gwtquery-core/src/main/java/com/google/gwt/query/client/plugins/events/SpecialEvent.java Переглянути файл

@@ -57,7 +57,7 @@ public interface SpecialEvent {

@Override
public boolean hasHandlers(Element e) {
return listener(e).hasHandlers(Event.getTypeInt(type), type);
return listener(e).hasHandlers(Event.getTypeInt(type), type, handler);
}

@Override

+ 9
- 3
gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryCoreTestGwt.java Переглянути файл

@@ -17,11 +17,9 @@ package com.google.gwt.query.client;

import static com.google.gwt.query.client.GQuery.$;
import static com.google.gwt.query.client.GQuery.$$;
import static com.google.gwt.query.client.GQuery.console;
import static com.google.gwt.query.client.GQuery.document;
import static com.google.gwt.query.client.GQuery.window;

import com.google.gwt.core.client.js.JsType;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
@@ -1032,11 +1030,19 @@ public class GQueryCoreTestGwt extends GWTTestCase {
}

public void testUtilsCallFunc() {
assertTrue(JsUtils.hasProperty(window, "document.body.style.background"));
assertTrue(JsUtils.hasProperty(window, "document.createElement"));
assertFalse(JsUtils.hasProperty(window, "document.body.style.foo"));

Element e = JsUtils.runJavascriptFunction(document, "createElement", "div");
assertNotNull(e);
assertEquals(e.getTagName().toLowerCase(), "div");

e = JsUtils.runJavascriptFunction(document, "foo", "bar", 2, true);
e = JsUtils.jsni("document.createElement", "span");
assertNotNull(e);
assertEquals(e.getTagName().toLowerCase(), "span");

e = JsUtils.jsni("document.foo.bar");
assertNull(e);
}


+ 0
- 2
gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryDeferredTestGwt.java Переглянути файл

@@ -263,7 +263,6 @@ public class GQueryDeferredTestGwt extends GWTTestCase {

$("button", e)
.click(new Function(){public void f() {
Fx.css3 = false;
$("p", e).append(" Started... ");
GQuery.when( effect ).done(new Function(){public void f() {
$("p", e).append(" Finished! ");
@@ -291,7 +290,6 @@ public class GQueryDeferredTestGwt extends GWTTestCase {

$("button", e)
.bind("click", new Function(){public void f() {
Fx.css3 = false;
$("p", e).append(" Started... ");

$("div",e).each(new Function(){public Object f(Element e, int i) {

+ 144
- 19
gwtquery-core/src/test/java/com/google/gwt/query/client/GQueryEffectsTestGwt.java Переглянути файл

@@ -15,7 +15,9 @@
*/
package com.google.gwt.query.client;

import static com.google.gwt.query.client.GQuery.*;
import static com.google.gwt.query.client.GQuery.$;
import static com.google.gwt.query.client.GQuery.$$;
import static com.google.gwt.query.client.GQuery.Effects;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
@@ -24,18 +26,24 @@ import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.query.client.GQuery.Offset;
import com.google.gwt.query.client.plugins.effects.Fx;
import com.google.gwt.query.client.plugins.Effects.GQAnimation;
import com.google.gwt.query.client.plugins.effects.Fx.ColorFx;
import com.google.gwt.query.client.plugins.effects.Fx.TransitFx;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.EasingCurve;
import com.google.gwt.query.client.plugins.effects.Transform;
import com.google.gwt.query.client.plugins.effects.Transitions;
import com.google.gwt.query.client.plugins.effects.TransitionsAnimation;
import com.google.gwt.query.client.plugins.effects.TransitionsAnimation.TransitionsClipAnimation;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

import java.util.Arrays;
import java.util.List;

/**
* Test class for testing gwtquery effects plugin api.
*/
@@ -55,7 +63,6 @@ public class GQueryEffectsTestGwt extends GWTTestCase {
}

public void gwtSetUp() {
Fx.css3 = false;
if (e == null) {
testPanel = new HTML();
RootPanel.get().add(testPanel);
@@ -257,11 +264,16 @@ public class GQueryEffectsTestGwt extends GWTTestCase {
assertEquals("cssprop=padding value=20px start=5 end=20 unit=px",
PropertiesAnimation.computeFxProp(g.get(0), "padding", "20px", false)
.toString());
assertEquals("cssprop=opacity value=show start=0 end=1 unit=",
PropertiesAnimation.computeFxProp(g.get(0), "opacity", "toggle", true)
.toString());
assertEquals("cssprop=opacity value=hide start=1 end=0 unit=",
PropertiesAnimation.computeFxProp(g.get(0), "opacity", "toggle", false)
.toString());

prop1 = GQuery.$$("marginTop: '-110px', marginLeft: '-110px', top: '50%', left: '50%', width: '174px', height: '174px', padding: '20px'");
PropertiesAnimation an = new PropertiesAnimation(EasingCurve.swing, g.get(0), prop1);
an.onStart();
an.onComplete();
GQAnimation an = new PropertiesAnimation().setEasing(EasingCurve.swing).setElement(g.get(0)).setProperties(prop1);
an.run(0);

assertEquals("cssprop=marginTop value=0 start=-110 end=0 unit=px",
PropertiesAnimation.computeFxProp(g.get(0), "marginTop", "0", false)
@@ -285,9 +297,8 @@ public class GQueryEffectsTestGwt extends GWTTestCase {
.toString());

prop1 = GQuery.$$("marginTop: '0', marginLeft: '0', top: '0%', left: '0%', width: '100px', height: '100px', padding: '5px'");
an = new PropertiesAnimation(EasingCurve.swing, g.get(0), prop1);
an.onStart();
an.onComplete();
an = new PropertiesAnimation().setEasing(EasingCurve.swing).setElement(g.get(0)).setProperties(prop1);
an.run(0);

assertEquals("cssprop=marginTop value=-110px start=0 end=-110 unit=px",
PropertiesAnimation.computeFxProp(g.get(0), "marginTop", "-110px",
@@ -312,6 +323,130 @@ public class GQueryEffectsTestGwt extends GWTTestCase {
.toString());
}

private void assertTransitFx(TransitFx fx, String prop, String val, String unit, String start, String end) {
assertEquals(prop, fx.cssprop);
assertEquals(val, fx.value);
assertEquals(unit, fx.unit);
if (!start.contains(".")) {
// discard decimals
assertEquals(start, fx.transitStart.replaceAll("\\.\\d+([a-z%]*)$", "$1"));
} else {
assertEquals(start, fx.transitStart);
}
if (!end.contains(".")) {
// discard decimals
assertEquals(end, fx.transitEnd.replaceAll("\\.\\d+([a-z%]*)$", "$1"));
} else {
assertEquals(end, fx.transitEnd);
}
}

public void testTransitionsCss() {
$(e).html("<div>");
Transitions t = $("div", e).as(Transitions.Transitions);

t.css("transform", "scale(1,2) rotateX(5deg) x(7) y(8)");
assertEquals("1,2", t.css("scale"));
assertEquals("7px", t.css("x"));
assertEquals("7px", t.css("translateX"));
t.css("y", "8");
assertEquals("8px", t.css("y"));
assertEquals("8px", t.css("translateY"));
}

public void testTransitionsAnimationComputeEffects() {
$(e)
.html(
"<div id='parent' style='background-color: yellow; width: 100px; height: 200px; top:130px; position: absolute; left: 130px'>"
+ "<p id='child' style='opacity: 0.7; background-color: pink; width: 100px; height: 100px; position: absolute; padding: 5px; margin: 0px'>Content 1</p></div>");
GQuery g = $("#child");
TransitFx f;

f = TransitionsAnimation.computeFxProp(g.get(0), "rotateY", "90deg", false);
assertTransitFx(f, "rotateY", "90deg", "deg", "0deg", "90deg");

f = TransitionsAnimation.computeFxProp(g.get(0), "marginTop", "-110px", false);
assertTransitFx(f, "marginTop", "-110px", "px", "0px", "-110px");

f = TransitionsAnimation.computeFxProp(g.get(0), "opacity", "toggle", false);
assertTransitFx(f, "opacity", "hide", "", "0.7", "0");

f = TransitionsAnimation.computeFxProp(g.get(0), "scaleX", "show", true);
assertTransitFx(f, "scaleX", "show", "", "0", "1");

f = TransitionsAnimation.computeFxProp(g.get(0), "width", "toggle", false);
assertTransitFx(f, "width", "hide", "px", "100px", "0px");

f = TransitionsAnimation.computeFxProp(g.get(0), "width", "+=45", false);
assertTransitFx(f, "width", "+=45", "px", "100px", "145px");

f = TransitionsAnimation.computeFxProp(g.get(0), "width", "100%", false);
assertTransitFx(f, "width", "100%", "%", "100px", "100%");
}

public void testTransformParser() {
Transform.has3d = true;
Transform t = new Transform("scaleZ(0.5) rotateZ(90deg) skewX(4) rotateY(45) scale(1, 1) x(10) y(12) z(14) matrix(1, 2,3 ,4)");
List<String> vals = Arrays.asList(t.toString().split(" "));
// scale(1,1) matrix(1,2,3,4) rotateZ(90deg) translateZ(14px) rotateY(45deg) translateY(12px) skewX(4deg) translateX(10px) scaleZ(0.5)
assertEquals(9, vals.size());
assertTrue(vals.contains("scaleZ(0.5)"));
assertTrue(vals.contains("rotateZ(90deg)"));
assertTrue(vals.contains("rotateY(45deg)"));
assertTrue(vals.contains("skewX(4deg)"));
assertTrue(vals.contains("rotateY(45deg)"));
assertTrue(vals.contains("scale(1,1)"));
assertTrue(vals.contains("translateX(10px)"));
assertTrue(vals.contains("translateY(12px)"));
assertTrue(vals.contains("translateZ(14px)"));
assertTrue(vals.contains("matrix(1,2,3,4)"));

Transform.has3d = false;
t = new Transform("scaleZ(0.5) rotateZ(90deg) skewX(4) rotateY(45) scale(1, 1) x(10) y(12) z(14) matrix(1, 2,3 ,4)");
vals = Arrays.asList(t.toString().split(" "));
// scale(1,1) matrix(1,2,3,4) translateY(12px) skewX(4deg) translateX(10px)
assertEquals(5, vals.size());
assertTrue(vals.contains("scale(1,1)"));
assertTrue(vals.contains("skewX(4deg)"));
assertTrue(vals.contains("translateX(10px)"));
assertTrue(vals.contains("translateY(12px)"));
assertTrue(vals.contains("matrix(1,2,3,4)"));
}

public void testTransitionsAnimation() {
final GQuery m = $("<div style='top: 10px; width:50px'>foo</div>").appendTo(e);

TransitionsClipAnimation a = new TransitionsClipAnimation();
a.setElement(m.get(0));
a.setProperties($$("clip-action: show, clip-origin: top-right, scaleZ: 0.5, delay: 30, left: 100, top: +=50, rotateZ: 90, rotateY: 45deg, easing: custom, duration: 400"));
a.onStart();

Properties from = a.getFxProperties(true);
Properties to = a.getFxProperties(false);

// HTMLUnit and chrome return different decimal part
assertEquals("0px", from.getStr("left").replace(".0", ""));
assertEquals("100px", to.getStr("left").replace(".0", ""));
assertEquals("10px", from.getStr("top").replace(".0", ""));
assertEquals("60px", to.getStr("top").replace(".0", ""));
assertEquals("0", from.getStr("rotateZ").replace(".0", ""));
assertEquals("90", to.getStr("rotateZ").replace(".0", ""));
assertEquals("0deg", from.getStr("rotateY").replace(".0", ""));
assertEquals("45deg", to.getStr("rotateY").replace(".0", ""));
assertEquals("0 0", from.getStr("scale").replace(".0", ""));
assertEquals("1 1", to.getStr("scale"));
assertNull(to.get("delay"));
assertNull(to.get("easing"));

// HTMLUnit and chrome return different values
assertTrue(m.attr("style").contains("rigin: 100% 0%") || m.attr("style").contains("rigin: right top"));
assertTrue(m.attr("style").contains("top: 10px"));

a.run(1);
assertTrue(m.attr("style").contains("rigin: 100% 0%") || m.attr("style").contains("rigin: right top"));
assertTrue(m.attr("style").contains("top: 60px"));
}

public void testColorEffectParsing(){
String html = "<div id='test' style='color: #112233'>Test</div>";
$(e).html(html);
@@ -385,16 +520,6 @@ public class GQueryEffectsTestGwt extends GWTTestCase {
timer.schedule(duration * 2);
}

public void testComputeFxPropTransitions() {
$(e).html("<div id='idtest' style='width: 200px; height 200px; border: solid 1px; position: absolute' ></div>");
final GQuery g = $("#idtest", e);

TransitFx fx = (TransitFx)TransitionsAnimation.computeFxProp(g.get(0), "width", "+=100", false);
assertEquals("200", fx.transitStart.replace(".0",""));
assertEquals("300", fx.transitEnd.replace(".0",""));
assertEquals("px", fx.unit);
}

public void testStop() {
$(e)
.html(

Завантаження…
Відмінити
Зберегти