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

com.google.gwt.query.client.GQuery Maven / Gradle / Ivy

There is a newer version: 1.5-beta1
Show newest version
/*
 * Copyright 2011, 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;

import static com.google.gwt.query.client.plugins.QueuePlugin.Queue;

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

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.JsArrayString;
import com.google.gwt.dom.client.*;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.HasCssName;
import com.google.gwt.query.client.css.CSS;
import com.google.gwt.query.client.css.HasCssValue;
import com.google.gwt.query.client.css.TakesCssValue;
import com.google.gwt.query.client.css.TakesCssValue.CssSetter;
import com.google.gwt.query.client.impl.AttributeImpl;
import com.google.gwt.query.client.impl.DocumentStyleImpl;
import com.google.gwt.query.client.impl.SelectorEngine;
import com.google.gwt.query.client.js.JsCache;
import com.google.gwt.query.client.js.JsMap;
import com.google.gwt.query.client.js.JsNamedArray;
import com.google.gwt.query.client.js.JsNodeArray;
import com.google.gwt.query.client.js.JsObjectArray;
import com.google.gwt.query.client.js.JsRegexp;
import com.google.gwt.query.client.js.JsUtils;
import com.google.gwt.query.client.plugins.Effects;
import com.google.gwt.query.client.plugins.Events;
import com.google.gwt.query.client.plugins.Plugin;
import com.google.gwt.query.client.plugins.Widgets;
import com.google.gwt.query.client.plugins.ajax.Ajax;
import com.google.gwt.query.client.plugins.ajax.Ajax.Settings;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.Easing;
import com.google.gwt.query.client.plugins.events.EventsListener;
import com.google.gwt.query.client.plugins.widgets.WidgetsUtils;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;

/**
 * GwtQuery is a GWT clone of the popular jQuery library.
 */
public class GQuery implements Lazy {

  private enum DomMan {
    AFTER, APPEND, BEFORE, PREPEND;
  }

  /**
   * A POJO used to store the top/left CSS positioning values of an element.
   */
  public static class Offset {
    public int left;
    public int top;

    public Offset(int left, int top) {
      this.left = left;
      this.top = top;
    }

    public Offset add(int left, int top) {
      return new Offset(this.left + left, this.top + top);
    }

    public String toString() {
      return top + "+" + left;
    }
  }

  /**
   * Class used internally to create DOM element from html snippet
   */
  private static class TagWrapper {
    public static final TagWrapper DEFAULT = new TagWrapper(0, "", "");
    private String postWrap;
    private String preWrap;
    private int wrapDepth;

    public TagWrapper(int wrapDepth, String preWrap, String postWrap) {
      this.wrapDepth = wrapDepth;
      this.postWrap = postWrap;
      this.preWrap = preWrap;
    }
  }

  /**
   * Implementation class to modify attributes.
   */
  protected static AttributeImpl attributeImpl;

  /**
   * The body element in the current page.
   */
  public static final BodyElement body = Document.get().getBody();

  /**
   * Object to store element data (public so as we can access to it from tests).
   */
  public static JsCache dataCache = null;

  /**
   * The document element in the current page.
   */
  public static final Document document = Document.get();

  /**
   * Static reference Effects plugin
   */
  public static Class Effects = com.google.gwt.query.client.plugins.Effects.Effects;

  /**
   * Implementation engine used for CSS selectors.
   */
  protected static SelectorEngine engine;

  /**
   * Static reference Events plugin
   */
  public static Class Events = com.google.gwt.query.client.plugins.Events.Events;

  /**
   * A static reference to the GQuery class.
   */
  public static Class GQUERY = GQuery.class;

  private static final String OLD_DATA_PREFIX = "old-";

  private static final String OLD_DISPLAY = OLD_DATA_PREFIX + "display";

  private static JsMap, Plugin> plugins;

  // Sizzle POS regex : usefull in some methods
  // TODO: Share this static with SelectorEngineSizzle
  private static final String POS_REGEX =
      ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^\\-]|$)";

  /**
   * Implementation class used for style manipulations.
   */
  private static DocumentStyleImpl styleImpl;

  private static JsRegexp tagNameRegex = new JsRegexp("<([\\w:]+)");

  /**
   * Static reference to the Widgets plugin
   */
  public static Class Widgets = com.google.gwt.query.client.plugins.Widgets.Widgets;

  /**
   * The window object.
   */
  public static final Element window = window();

  private static Element windowData = null;

  private static JsNamedArray wrapperMap;

  /**
   * Create an empty GQuery object.
   */
  public static GQuery $() {
    return new GQuery(JsNodeArray.create());
  }

  /**
   * Wrap a GQuery around an existing element.
   */
  public static GQuery $(Element element) {
    return new GQuery(element);
  }

  /**
   * Wrap a GQuery around an event's target element.
   */
  public static GQuery $(Event event) {
    return event == null ? $() : $((Element) event.getCurrentEventTarget().cast());
  }

  /**
   * Wrap a GQuery around the element of a Function callback.
   */
  public static GQuery $(Function f) {
    return $(f.getElement());
  }

  /**
   * Wrap a GQuery around an existing javascript element, event, node, nodelist, function or array.
   */
  public static GQuery $(JavaScriptObject jso) {
    if (jso == null) {
      return $();
    }
    // Execute a native javascript function like jquery does
    if (JsUtils.isFunction(jso)) {
      new JsUtils.JsFunction(jso).fe();
      return $();
    }
    // Wraps a native array like jquery does
    if (!JsUtils.isWindow(jso) && !JsUtils.isElement(jso) && JsUtils.isArray(jso)) {
      JsArrayMixed c = jso.cast();
      JsNodeArray elms = JsNodeArray.create();
      for (int i = 0; i < c.length(); i++) {
        Object obj = c.getObject(i);
        if (obj instanceof Node) {
          elms.addNode((Node) obj);
        }
      }
      return $((NodeList)elms);
    }

    return JsUtils.isWindow(jso) ? $(jso. cast()) :
      JsUtils.isElement(jso) ? $(jso. cast()) :
      JsUtils.isEvent(jso) ? $(jso. cast()) :
      JsUtils.isNodeList(jso) ? $(jso.> cast()) : $();
  }

  /**
   * Wrap a GQuery around any object, supported objects are:
   *   String, GQuery, Function, Widget, JavaScriptObject
   *
   * In the case of string, we accept a CSS selector which is then used to match a set of
   * elements, or a raw HTML to create a GQuery element containing those elements. Xpath
   * selector is supported in browsers with native xpath engine.
   *
   * In the case of a JavaScriptObject we handle:
   *   Element, Event, Node, Nodelist and native functions or arrays.
   *
   * If the case of a native function, we execute it and return empty.
   */
  public static GQuery $(Object o) {
    if (o != null) {
      if (o instanceof String) {
        return $((String)o);
      }
      if (o instanceof GQuery) {
        return (GQuery)o;
      }
      if (o instanceof Function) {
        return $((Function)o);
      }
      if (o instanceof JavaScriptObject) {
        return $((JavaScriptObject)o);
      }
      if (o instanceof IsWidget) {
        return $(Arrays.asList(o));
      }
      System.err.println("GQuery.$(Object o) could not wrap the type : " + o.getClass());
    }
    return $();
  }

  /**
   * Create a new GQuery given a list of nodes, elements or widgets
   */
  public static GQuery $(List nodesOrWidgets) {
    JsNodeArray elms = JsNodeArray.create();
    if (nodesOrWidgets != null) {
      for (Object o : nodesOrWidgets) {
        if (o instanceof Node) {
          elms.addNode((Node) o);
        } else if (o instanceof IsWidget) {
          elms.addNode(((IsWidget)o).asWidget().getElement());
        }
      }
    }
    return new GQuery(elms);
  }

  /**
   * Wrap a GQuery around an existing node.
   */
  public static GQuery $(Node n) {
    return $((Element) n);
  }

  /**
   * Wrap a GQuery around existing Elements.
   */
  public static GQuery $(NodeList elms) {
    return new GQuery(elms);
  }

  /**
   * This function accepts a string containing a CSS selector which is then used to match a set of
   * elements, or it accepts raw HTML creating a GQuery element containing those elements. Xpath
   * selector is supported in browsers with native xpath engine.
   */
  public static GQuery $(String selectorOrHtml) {
    return $(selectorOrHtml, document);
  }

  /**
   * This function accepts a string containing a CSS selector which is then used to match a set of
   * elements, or it accepts raw HTML creating a GQuery element containing those elements. The
   * second parameter is is a class reference to a plugin to be used.
   *
   * Xpath selector is supported in browsers with native xpath engine.
   */
  public static  T $(String selector, Class plugin) {
    return $(selector, document, plugin);
  }

  /**
   * This function accepts a string containing a CSS selector which is then used to match a set of
   * elements, or it accepts raw HTML creating a GQuery element containing those elements. The
   * second parameter is the context to use for the selector, or the document where the new elements
   * will be created.
   *
   * Xpath selector is supported in browsers with native xpath engine.
   */
  public static GQuery $(String selectorOrHtml, Node ctx) {
    String selector = null;
    if (selectorOrHtml == null || (selector = selectorOrHtml.trim()).length() == 0) {
      return $();
    }
    if (selector.startsWith("<")) {
      return innerHtml(selectorOrHtml, JsUtils.getOwnerDocument(ctx));
    }
    return new GQuery().select(selectorOrHtml, ctx);
  }

  /**
   * This function accepts a string containing a CSS selector which is then used to match a set of
   * elements, or it accepts raw HTML creating a GQuery element containing those elements. The
   * second parameter is the context to use for the selector. The third parameter is the class
   * plugin to use.
   *
   * Xpath selector is supported in browsers with native xpath engine.
   */
  @SuppressWarnings("unchecked")
  public static  T $(String selector, Node context, Class plugin) {
    try {
      if (plugins != null) {
        T gquery = (T) plugins.get(plugin).init(new GQuery().select(selector, context));
        return gquery;
      }
      throw new RuntimeException("No plugin for class " + plugin);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * This function accepts a string containing a CSS selector which is then used to match a set of
   * elements, or it accepts raw HTML creating a GQuery element containing those elements. The
   * second parameter is the context to use for the selector, or the document where the new elements
   * will be created.
   *
   * Xpath selector is supported in browsers with native xpath engine.
   */
  public static GQuery $(String selectorOrHtml, Widget context) {
    return $(selectorOrHtml, context.getElement());
  }

  /**
   * This function accepts a string containing a CSS selector which is then used to match a set of
   * elements, or it accepts raw HTML creating a GQuery element containing those elements. The
   * second parameter is the context to use for the selector. The third parameter is the class
   * plugin to use.
   *
   * Xpath selector is supported in browsers with native xpath engine.
   */
  public static  T $(String selector, Widget context, Class plugin) {
    return $(selector, context.getElement(), plugin);
  }

  /**
   * Wrap a GQuery around one widget or an array of existing ones.
   */
  public static GQuery $(Widget... widgets) {
    return $(Arrays.asList(widgets));
  }

  /**
   * Wrap a JSON object.
   */
  public static Properties $$(String properties) {
    return Properties.create(properties);
  }

  /**
   * Perform an ajax request to the server.
   */
  public static void ajax(Properties p) {
    ajax(p);
  }

  /**
   * Perform an ajax request to the server.
   */
  public static void ajax(Settings settings) {
    Ajax.ajax(settings);
  }

  /**
   * Perform an ajax request to the server.
   */
  public static void ajax(String url, Settings settings) {
    Ajax.ajax(url, settings);
  }

  @SuppressWarnings("unchecked")
  protected static GQuery cleanHtmlString(String elem, Document doc) {

    String tag = tagNameRegex.exec(elem).get(1);

    if (tag == null) {
      throw new RuntimeException("HTML snippet doesn't contain any tag");
    }

    if (wrapperMap == null) {
      initWrapperMap();
    }

    TagWrapper wrapper = wrapperMap.get(tag.toLowerCase());

    if (wrapper == null) {
      wrapper = TagWrapper.DEFAULT;
    }

    // TODO: fix IE link tag serialization
    // TODO: fix IE