package xapi.components.impl; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.UnsafeNativeLong; import elemental.dom.Element; import elemental.html.DivElement; import elemental.js.util.JsArrayOfBoolean; import elemental.js.util.JsArrayOfInt; import elemental.js.util.JsArrayOfNumber; import elemental.js.util.JsArrayOfString; import xapi.components.api.Console; import xapi.components.api.Document; import xapi.components.api.JsObject; public class JsSupport { public static native Document doc() /*-{ return $doc; }-*/; public static native JsObject object() /*-{ return Object; }-*/; public static native Console console() /*-{ return $wnd.console; }-*/; public static native Byte boxByte(JavaScriptObject o) /*-{ return typeof o === 'number' ? @java.lang.Byte::new(B)(~~o) : typeof o === 'string' ? @java.lang.Byte::new(Ljava/lang/String;)(o) : o; }-*/; public static native Short boxShort(JavaScriptObject o) /*-{ return typeof o === 'number' ? @java.lang.Short::new(S)(~~o) : typeof o === 'string' ? @java.lang.Short::new(Ljava/lang/String;)(o) : o; }-*/; public static native Integer boxInteger(JavaScriptObject o) /*-{ return typeof o === 'number' ? @java.lang.Integer::new(I)(~~o) : typeof o === 'string' ? @java.lang.Integer::new(Ljava/lang/String;)(o) : o; }-*/; public static native Float boxFloat(JavaScriptObject o) /*-{ return typeof o === 'number' ? @java.lang.Float::new(F)(o) : typeof o === 'string' ? @java.lang.Float::new(Ljava/lang/String;)(o) : o; }-*/; public static native Double boxDouble(JavaScriptObject o) /*-{ return typeof o === 'number' ? @java.lang.Double::new(D)(o) : typeof o === 'string' ? @java.lang.Double::new(Ljava/lang/String;)(o) : o; }-*/; public static native Character boxCharacter(JavaScriptObject o) /*-{ return typeof o === 'number' ? @java.lang.Character::new(C)(o) : typeof o === 'string' ? @java.lang.Character::new(C)(o && o.charAt(0) || 0) : o; }-*/; public static native Boolean boxBoolean(JavaScriptObject o) /*-{ if (typeof o === 'string') { o = o === 'true'; } return typeof o === 'boolean' ? o ? @java.lang.Boolean::TRUE : @java.lang.Boolean::FALSE : o; }-*/; @UnsafeNativeLong public static native Long boxLong(JavaScriptObject o) /*-{ if (typeof o === 'number') { o = ~~o + ''; } if (typeof o === 'string') { return @java.lang.Long::new(Ljava/lang/String;)(o).@java.lang.Long::longValue()(); } if (o.l == undefined) { // Can fail if o is not a number, but it will never fail if o is a primitive long return o.@java.lang.Number::longValue()(); } return o; }-*/; @UnsafeNativeLong public static native String boxArray(JavaScriptObject o, String joiner) /*-{ if (!o) { return ""; } if (!joiner) { joiner = " "; // If using default joiner, let's eat all double spaces in any input strings. // Though the string will lose the ability to have double spacing, it won't have strange breakage. // If you need to support double spaces, be sure to supply your own joiner string, // if using generated code, a @JsProperty String joiner(); method should suffice. for (var i = o.length; i-->0;) { if (o[i]&&o[i].replace) { o[i] = o[i].replace(/([ ][ ]+)/ , " "); } } } if (o.join) { return o.join(joiner); } return o.toString(); }-*/; public static native byte unboxByte(JavaScriptObject o) /*-{ if (o == undefined) { return 0; } if (typeof o === 'string') { o = parseInt(o); } return typeof o === 'number' ? ~~o : o.@java.lang.Number::byteValue()(); }-*/; public static native short unboxShort(JavaScriptObject o) /*-{ if (o == undefined) { return 0; } if (typeof o === 'string') { o = parseInt(o); } return typeof o === 'number' ? ~~o : o.@java.lang.Number::shortValue()(); }-*/; public static native int unboxInteger(JavaScriptObject o) /*-{ if (o == undefined) { return 0; } if (typeof o === 'string') { o = parseInt(o); } return typeof o === 'number' ? ~~o : o.@java.lang.Number::intValue()(); }-*/; public static native float unboxFloat(JavaScriptObject o) /*-{ if (o == undefined) { return 0; } if (typeof o === 'string') { o = parseFloat(o); } return typeof o === 'number' ? o : o.@java.lang.Number::floatValue()(); }-*/; public static native double unboxDouble(JavaScriptObject o) /*-{ if (o == undefined) { return 0; } if (typeof o === 'string') { o = parseFloat(o); } return typeof o === 'number' ? o : o.@java.lang.Number::doubleValue()(); }-*/; public static native char unboxCharacter(JavaScriptObject o) /*-{ if (o == undefined) { return 0; } if (typeof o === 'string') { o = o && o.charAt(0) || 0; } return typeof o === 'number' ? o : o.@java.lang.Character::charValue()(); }-*/; public static native boolean unboxBoolean(JavaScriptObject o) /*-{ if (o == undefined) { return false; } return typeof o === 'boolean' ? o : typeof o === 'string' ? o === 'true' : o.@java.lang.Boolean::booleanValue()(); }-*/; @UnsafeNativeLong public static native long unboxLong(JavaScriptObject o) /*-{ if (o == undefined) { o = '0'; } if (typeof o === 'number') { o = o.toString(); } if (typeof o === 'string') { return @java.lang.Long::new(Ljava/lang/String;)(o).@java.lang.Long::longValue()(); } if (o.l == undefined) { return o.@java.lang.Number::longValue()(); } return o; }-*/; /** * Constructs a "fake number" that works in both GWT and javascript. * * This number has the format java expects: {l:0, m:0, h:0}, but also * overrides the .valueOf() function to act like a Long.doubleValue() in * javascript. * * Operations in javascript will lose precision due to the use of 52 bit * numbers, so if you need full long precision, use only methods originating * from java that use the object format. * * @param o * -> A number in any format that can be expected from javascript * (number, string, Long or long) * @return a hybrid long with shims to act like a javascript number. */ @UnsafeNativeLong public static native long unboxLongNative(JavaScriptObject o) /*-{ var javaLong = @xapi.components.impl.JsSupport::unboxLong(Lcom/google/gwt/core/client/JavaScriptObject;)(o); var tricky = function() { return @java.lang.Long::new(J)(javaLong).@java.lang.Long::doubleValue()(); } tricky.l = javaLong.l; tricky.m = javaLong.m; tricky.h = javaLong.h; tricky.valueOf = function() { return @java.lang.Long::new(J)(javaLong).@java.lang.Long::doubleValue()(); } return tricky; }-*/; public static native JsArrayOfString unboxArrayOfString(JavaScriptObject o, String joiner) /*-{ if (!o) { return []; } if (!joiner) { joiner = " "; } return o.toString().split(joiner); }-*/; public static native JsArrayOfInt unboxArrayOfInt(JavaScriptObject o, String joiner) /*-{ if (!o) { return []; } if (!joiner) { joiner = " "; } var arr = o.toString().split(joiner); for (var i = arr.length; i-->0;) { arr[i] = parseInt(arr[i]); } return arr; }-*/; public static native JsArrayOfNumber unboxArrayOfNumber(JavaScriptObject o, String joiner) /*-{ if (!o) { return []; } if (!joiner) { joiner = " "; } var arr = o.toString().split(joiner); for (var i = arr.length; i-->0;) { arr[i] = parseFloat(arr[i]); } return arr; }-*/; public static native JsArrayOfBoolean unboxArrayOfBoolean(JavaScriptObject o, String joiner) /*-{ if (!o) { return []; } if (!joiner) { joiner = " "; } var arr = o.toString().split(joiner); for (var i = arr.length; i-->0;) { arr[i] = arr[i] === 'true'; } return arr; }-*/; public static native boolean not(Object o) /*-{ return !o; }-*/; public static native boolean is(Object o) /*-{ return !!o; }-*/; public static native boolean jsEquals(Object o1, Object o2) /*-{ return o1 === o2; }-*/; public static native boolean jsNotEquals(Object o1, Object o2) /*-{ return o1 !== o2; }-*/; public static String newId() { Document doc = doc(); String id = randomId(); // Gets a new, unused id. // This is preferable to using a static int, // as there may be more than one compiled app running in the page. while (doc.getElementById(id) != null) { id = randomId(); } return id; } private static native String randomId() /*-{ // We want a random number that starts with a letter. // We will do this using base 36 toString method, // and we will use a number between 0.3 and 0.99999 var i = 0.3 + 0.7 * Math.random(); // Ditch the leading "0." return i.toString(36).substr(2); }-*/; public static <E extends Element> E newElement(String tagName) { return doc().createElement(tagName); } public static DivElement newDivWithHtml(String html) { DivElement div = newElement("div"); div.setInnerHTML(html); return div; } public static <J extends JavaScriptObject> J create() { return JavaScriptObject.createObject().cast(); } public static native Element setAttr(Element group, String string) /*-{ group.setAttribute(string, ""); return group; }-*/; public static void hideIfEmpty(Element e) { if (e.getInnerText().isEmpty()) { e.getStyle().setDisplay("none"); } else { e.getStyle().removeProperty("display"); } } public static native boolean isFunction(Object a) /*-{ return typeof a === 'function'; }-*/; public static native <A> A invoke(Object func, Object args) /*-{ return func.call(args); }-*/; public static native boolean exists(Object object, String name) /*-{ return name in object; }-*/; public static native boolean attributeExists(Object object, String name) /*-{ return object.getAttribute && object.getAttribute(name) != null; }-*/; public static native boolean attributeEquals(Object object, String name, String value) /*-{ $wnd.console.log(object, name, value); return object.getAttribute && object.getAttribute(name) === value; }-*/; public static native void setBoolean(Object object, String key, boolean value) /*-{ object[key]=value; }-*/; public static native void setByte(Object object, String key, byte value) /*-{ object[key]=value; }-*/; public static native void setShort(Object object, String key, short value) /*-{ object[key]=value; }-*/; public static native void setChar(Object object, String key, char value) /*-{ object[key]=value; }-*/; public static native void setInt(Object object, String key, int value) /*-{ object[key]=value; }-*/; @UnsafeNativeLong public static native void setLong(Object object, String key, long value) /*-{ object[key]=value; }-*/; public static native void setFloat(Object object, String key, float value) /*-{ object[key]=value; }-*/; public static native void setDouble(Object object, String key, double value) /*-{ object[key]=value; }-*/; public static native void setObject(Object object, String key, Object value) /*-{ object[key]=value; }-*/; public static native Object getObject(Object object, String key) /*-{ return object[key]; }-*/; public static native String getString(Object object, String key) /*-{ return object[key]||null;//coerce undefined }-*/; public static native int getInt(Object object, String key) /*-{ var i = object[key]||null; if (i) { if (typeof i === 'number') { return ~~i; } return @xapi.components.impl.JsSupport::unboxInteger(Lcom/google/gwt/core/client/JavaScriptObject;)(i); } return 0; }-*/; public static native int getDouble(Object object, String key) /*-{ var i = object[key]||null; if (i) { if (typeof i === 'number') { return i; } return @xapi.components.impl.JsSupport::unboxDouble(Lcom/google/gwt/core/client/JavaScriptObject;)(i); } return 0; }-*/; @UnsafeNativeLong public static native long getLong(Object object, String key) /*-{ var i = object[key]||null; if (i) { if (typeof i === 'object' && typeof i.l === 'number') { return i; } return @xapi.components.impl.JsSupport::unboxLong(Lcom/google/gwt/core/client/JavaScriptObject;)(i); } return {l:0, m:0, h:0}; }-*/; public static native boolean getBoolean(Object object, String key) /*-{ var i = object[key]||null; if (i) { if (typeof i === 'boolean') { return i; } return @xapi.components.impl.JsSupport::unboxBoolean(Lcom/google/gwt/core/client/JavaScriptObject;)(i); } return false; }-*/; public static native Element createShadowRoot(Element e) /*-{ return e.createShadowRoot(); }-*/; public static native JsArrayOfString split(String value, String on) /*-{ return value.split(on); }-*/; public static void addClassName(Element e, String className) { String was = e.getClassName(); if (was.isEmpty()) { e.setClassName(className); } else if (!((" " + was + " ").contains(" " + className + " "))) { e.setClassName(was + " " + className); } } public static String interleave(JsArrayOfString array, String key) { if (array.isEmpty()) return ""; return key + " " + array.join(" " + key) + " "; } public static void removeFromParent(Element item) { Element parent = item.getParentElement(); if (parent != null) { parent.removeChild(item); } } public static native Element findInShadowRoot(Element element, String id) /*-{ return element.shadowRoot.getElementById(id); }-*/; public static native JavaScriptObject copy(Object o) /*-{ return Object.create(o); }-*/; }