com.google.gwt.query.client.js.JsUtils Maven / Gradle / Ivy
The 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.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;
import com.google.gwt.dom.client.NodeList;
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.user.client.Command;
import com.google.gwt.user.client.DOM;
/**
* A bunch of utility methods for GQuery.
*/
public class JsUtils {
/**
* A Function which wraps a javascript function.
*/
public static class JsFunction extends Function implements Command {
private JavaScriptObject jso = null;
public JsFunction(JavaScriptObject f) {
if (JsUtils.isFunction(f)) {
jso = f;
}
}
public boolean equals(Object obj) {
return jso.equals(obj);
}
public int hashCode() {
return jso.hashCode();
}
private native Object exec(JavaScriptObject f, Object data) /*-{
return @com.google.gwt.query.client.js.JsCache::gwtBox(*)([ f(data) ]);
}-*/;
public void f() {
if (jso != null) {
setArguments(exec(jso, arguments(0)));
}
}
public void execute() {
f();
}
}
/**
* Wraps a GQuery function into a native javascript one so as we can
* export Java methods without using JSNI.
*/
public static native JavaScriptObject wrapFunction(Function f) /*-{
return function(r) {
var o = @java.util.ArrayList::new()();
for (i in arguments) {
r = @com.google.gwt.query.client.js.JsCache::gwtBox(*)([arguments[i]]);
[email protected]::add(Ljava/lang/Object;)(r);
}
o = [email protected]::toArray()();
[email protected]::setArguments([Ljava/lang/Object;)(o);
return [email protected]::fe([Ljava/lang/Object;)(o);
}
}-*/;
/**
* Default JsUtils implementation.
*/
public static class JsUtilsImpl {
public Properties parseJSON(String json) {
return JsonUtils.safeEval(json);
}
public native String JSON2String(JavaScriptObject o) /*-{
return $wnd.JSON.stringify(o);
}-*/;
public native Element parseXML(String xml) /*-{
return new DOMParser().parseFromString(xml, "text/xml").documentElement;
}-*/;
public String text(Element e) {
return e.getInnerText();
}
public JsArray unique(JsArray a) {
JsArray ret = JavaScriptObject.createArray().cast();
JsCache cache = JsCache.create();
for (int i = 0; i < a.length(); i++) {
Element e = a.get(i);
int id = e.hashCode();
if (!cache.exists(id)) {
cache.putNumber(id, 1);
ret.push(e);
}
}
return ret;
}
public native String XML2String(JavaScriptObject o) /*-{
return (new XMLSerializer()).serializeToString(o);
}-*/;
}
/**
* IE JsUtils implemetation.
*/
public static class JsUtilsImplIE6 extends JsUtilsImpl {
@Override
public Properties parseJSON(String json) {
return JsonUtils.unsafeEval(json);
}
@Override
public String JSON2String(JavaScriptObject js) {
// This is a very basic implementation for IE6/IE7 of JSON.stringify
// If many people demand a better one we could consider to use json2.js
// @see https://github.com/douglascrockford/JSON-js/blob/master/json2.js
Properties prop = js.cast();
String ret = "";
for (String k : prop.keys()) {
String ky = k.matches("\\d+") ? k : "\"" + k + "\"";
JsCache o = prop.getArray(k).cast();
if (o != null) {
ret += ky + ":[";
for (int i = 0, l = o.length(); i < l; i++) {
Properties p = o. cast().getJavaScriptObject(i);
if (p != null) {
ret += p.toJsonString() + ",";
} else {
ret += "\"" + o.getString(i) + "\",";
}
}
ret += "],";
} else {
Properties p = prop.getJavaScriptObject(k);
if (p != null) {
ret += ky + ":" + p.toJsonString() + ",";
} else {
ret += ky + ":\"" + prop.getStr(k) + "\",";
}
}
}
return "{" + ret.replaceAll(",\\s*([\\]}]|$)", "$1")
.replaceAll("([:,\\[])\"(-?[\\d\\.]+|null|false|true)\"", "$1$2")
+ "}";
}
@Override
public native Element parseXML(String xml) /*-{
var d = new ActiveXObject("Microsoft.XmlDom");
d.loadXML(xml);
return d.documentElement;
}-*/;
@Override
public String text(Element e) {
return isXML(e) ? xmlText(e) : super.text(e);
}
@Override
public JsArray unique(JsArray a) {
// in IE6 XML elements does not support adding hashId to the object
if (browser.ie6 && isXML(a.get(0))) {
return a;
}
return super.unique(a);
}
@Override
public native String XML2String(JavaScriptObject o) /*-{
return o.xml;
}-*/;
private native String xmlText(Element e) /*-{
return e.text;
}-*/;
}
private static JsUtilsImpl utilsImpl = GWT.create(JsUtilsImpl.class);
/**
* Returns a property present in a javascript object.
*/
public static T prop(JavaScriptObject o, Object id, Class extends T> type) {
return o == null ? null : o. cast().get(id, type);
}
/**
* Returns a property present in a javascript object.
*/
public static T prop(JavaScriptObject o, Object id) {
return o == null ? null : o. cast(). get(id);
}
/**
* Set a property to a javascript object.
*/
public static void prop(JavaScriptObject o, Object id, Object val) {
if (o != null) {
o. cast().put(id, val);
}
}
/**
* Execute a native javascript function.
*/
public static T exec(JavaScriptObject jsFunction, Object... args) {
assert isFunction(jsFunction);
return jsni(jsFunction, "call", jsFunction, args);
}
/**
* Assign a function to a property of the window object.
*/
public static void export(String name, Function f) {
export(GQuery.window, name, f);
}
/**
* Export a function as a property of a javascript object.
*/
public static void export(JavaScriptObject o, String name, Function f) {
prop(o, name, (Object)(f != null ? wrapFunction(f) : null));
}
/**
* Camelize style property names.
* for instance: font-name -> fontName
*/
public static native String camelize(String s) /*-{
return s.replace(/\-(\w)/g, function(all, letter) {
return letter.toUpperCase();
});
}-*/;
/**
* Merge the oldNodes list into the newNodes one. If oldNodes is null, a new
* list will be created and returned. If oldNodes is not null, a new list will
* be created depending on the create flag.
*/
public static NodeList copyNodeList(NodeList oldNodes,
NodeList newNodes, boolean create) {
NodeList ret = oldNodes == null || create ? JsNodeArray.create()
: oldNodes;
JsCache idlist = JsCache.create();
for (int i = 0; oldNodes != null && i < oldNodes.getLength(); i++) {
Element e = oldNodes.getItem(i);
idlist.put(e.hashCode(), 1);
if (create) {
ret. cast().addNode(e, i);
}
}
for (int i = 0, l = newNodes.getLength(), j = ret.getLength(); i < l; i++) {
Element e = newNodes.getItem(i);
if (!idlist.exists(e.hashCode())) {
ret. cast().addNode(newNodes.getItem(i), j++);
}
}
return ret;
}
/**
* Use the method in the gquery class.
* $(elem).cur(prop, force);
*/
@Deprecated
public static double cur(Element elem, String prop, boolean force) {
return GQuery.$(elem).cur(prop, force);
}
/**
* Compare two numbers using javascript equality.
*/
public static native boolean eq(double s1, double s2) /*-{
return s1 == s2;
}-*/;
/**
* Compare two objects using javascript equality.
*/
public static native boolean eq(Object s1, Object s2) /*-{
return s1 == s2;
}-*/;
/**
* Returns the owner document element of an element.
*/
public static Document getOwnerDocument(Node n) {
return n == null || !isElement(n) ? null : n.getNodeType() == Node.DOCUMENT_NODE
? n. cast() : n.getOwnerDocument();
}
/**
* Check if an object has a property with name
defined.
* It supports dots in the name meaning checking nested properties.
*
* Example:
*
* // Check whether a browser supports touch events
* hasProperty(window, "ontouchstart");
*
*/
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;
}-*/;
/**
* Check whether an element has an attribute, this is here since GWT Element.getAttribute
* implementation returns an empty string instead of null when the attribute is not
* present.
*/
public static native boolean hasAttribute(Element o, String name) /*-{
return !!(o && o.getAttribute(name) !== null);
}-*/;
/**
* Hyphenize style property names.
* for instance: fontName -> font-name
*/
public static native String hyphenize(String name) /*-{
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
}-*/;
/**
* Check is a javascript object can be used as an array.
*/
public static native boolean isArray(JavaScriptObject o) /*-{
return Object.prototype.toString.call(o) == '[object Array]'
|| typeof o.length == 'number';
}-*/;
/**
* Check is a javascript object is a FormData.
*/
public static native boolean isFormData(JavaScriptObject o) /*-{
return Object.prototype.toString.call(o) == '[object FormData]';
}-*/;
/**
* Return whether the event was prevented.
*/
public static native boolean isDefaultPrevented(JavaScriptObject e) /*-{
return e.defaultPrevented || e.returnValue === false || e.getPreventDefault
&& e.getPreventDefault() ? true : false;
}-*/;
/**
* 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) {
assert n != null;
if ("html".equalsIgnoreCase(n.getNodeName())) {
return false;
}
return !getOwnerDocument(n).getBody().isOrHasChild(n);
}
/**
* Check is a javascript object can be cast to an Element.
*/
public static native boolean isElement(Object o) /*-{
return !!o && 'nodeType' in o && 'nodeName' in o;
}-*/;
/**
* Check is a javascript object can be cast to an Event.
*/
public static boolean isEvent(JavaScriptObject o) {
return hasProperty(o, "currentTarget");
}
/**
* Check is a javascript object is a function.
*/
public static native boolean isFunction(JavaScriptObject o) /*-{
return Object.prototype.toString.call(o) == '[object Function]';
}-*/;
/**
* Check is a javascript can be cast to a node list.
*/
public static native boolean isNodeList(JavaScriptObject o) /*-{
var r = Object.prototype.toString.call(o);
return r == '[object HTMLCollection]' || r == '[object NodeList]'
|| (typeof o == 'object' && o.length && o[0] && o[0].tagName) ? true : false;
}-*/;
/**
* Check is a javascript object is a Window.
*/
public static boolean isWindow(JavaScriptObject o) {
return hasProperty(o, "alert");
}
/**
* Check if an element is a DOM or a XML node.
*/
public static boolean isXML(Node o) {
return o == null ? false
: !"HTML".equals(getOwnerDocument(o).getDocumentElement().getNodeName());
}
/**
* Load an external javascript library. The inserted script replaces the
* element with the given id in the document.
*
* @deprecated use {@link com.google.gwt.query.client.plugins.ajax.Ajax#loadScript(String)}
*/
@Deprecated
public static void loadScript(String url, String id) {
GQuery gs = GQuery.$(DOM.createElement("script"));
GQuery gp = GQuery.$("#" + id).parent();
if (gp.size() != 1) {
gp = GQuery.$(GQuery.document.getBody());
}
GQuery.$("#" + id).remove();
gp.append(gs.attr("src", url).attr("type", "text/javascript").attr("id", id));
}
/**
* Return the element which is truth in the double scope.
*/
public static native double or(double s1, double s2) /*-{
return s1 || s2;
}-*/;
/**
* Return the element which is truth in the javascript scope.
*/
public static native T or(T s1, T s2) /*-{
return s1 || s2;
}-*/;
/**
* Parses a json string returning a Object with useful method to get the
* content.
*/
public static Properties parseJSON(String json) {
try {
return utilsImpl.parseJSON(json);
} catch (Exception e) {
if (!GWT.isProdMode()) {
System.err.println("Error while parsing json: " + e.getMessage() + ".\n" + json);
}
return Properties.create();
}
}
/**
* Parses a xml string and return the xml document element which can then be
* passed to GQuery to create a typical GQuery object that can be traversed
* and manipulated.
*/
public static Element parseXML(String xml) {
return utilsImpl.parseXML(xml);
}
public static String text(Element e) {
return utilsImpl.text(e);
}
/**
* Utility method to cast objects in production.
* Useful for casting native implementations to interfaces like JsInterop
*/
public static native T cast(Object o) /*-{
return o;
}-*/;
/**
* Utility method to cast objects to array of string in production.
*/
public static native String[] castArrayString(Object a) /*-{
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
*
* // 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);
*
*
* @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 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
*
* // 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);
*
*
* @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 jsni(String meth, Object... args) {
return runJavascriptFunction(null, meth, args);
}
/**
* Call via jsni any arbitrary function present in a Javascript object.
*
* It's thought for avoiding to create jsni methods to call external functions and
* facilitate the writing of js wrappers.
*
* Example
*
* // Create a svg node in our document.
* Element svg = runJavascriptFunction(document, "createElementNS", "http://www.w3.org/2000/svg", "svg");
* // Append it to the dom
* $(svg).appendTo(document);
*
*
* @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 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 runJavascriptFunction(JavaScriptObject o, String meth, Object... args) {
return runJavascriptFunctionImpl(o, meth, JsObjectArray.create().add(args).cast());
}
private static native T runJavascriptFunctionImpl(JavaScriptObject o, String meth, JsArrayMixed args) /*-{
var f = o || $wnd, 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)]);
}-*/;
/**
* Check if a number is true in the javascript scope.
*/
public static native boolean truth(double a) /*-{
return a ? true : false;
}-*/;
/**
* Check if an object is true in the javascript scope.
*/
public static native boolean truth(Object a) /*-{
return a ? true : false;
}-*/;
/**
* Remove duplicates from an elements array.
*/
public static JsArray unique(JsArray a) {
return utilsImpl.unique(a);
}
public static String XML2String(JavaScriptObject js) {
return utilsImpl.XML2String(js);
}
public static String JSON2String(JavaScriptObject js) {
return utilsImpl.JSON2String(js);
}
/**
* Returns a QueryString representation of a JavascriptObject.
*
* TODO: jquery implementation accepts a second parameter (traditional)
*/
public static String param(JavaScriptObject js) {
Properties prop = js.cast();
String ret = "";
for (String k : prop.keys()) {
ret += ret.isEmpty() ? "" : "&";
JsCache o = prop.getArray(k).cast();
if (o != null) {
for (int i = 0, l = o.length(); i < l; i++) {
ret += i > 0 ? "&" : "";
Properties p = o. cast().getJavaScriptObject(i);
if (p != null) {
ret += k + "[]=" + p.toJsonString();
} else {
ret += k + "[]=" + o.getString(i);
}
}
} else {
Properties p = prop.getJavaScriptObject(k);
if (p != null) {
ret += k + "=" + p.tostring();
} else {
String v = prop.getStr(k);
if (v != null && !v.isEmpty() && !"null".equalsIgnoreCase(v)) {
ret += k + "=" + v;
}
}
}
}
return ret;
}
}