Skip to content

Commit

Permalink
Implement JavaScript renderer support (#15485)
Browse files Browse the repository at this point in the history
Change-Id: Ifeac12d4124a4a7e5d0c143ff5c0590a2c98509d
  • Loading branch information
Legioth authored and Vaadin Code Review committed Jan 13, 2015
1 parent 30f0e91 commit 5102cc9
Show file tree
Hide file tree
Showing 10 changed files with 619 additions and 10 deletions.
13 changes: 8 additions & 5 deletions client/src/com/vaadin/client/JavaScriptConnectorHelper.java
Expand Up @@ -50,7 +50,7 @@ public class JavaScriptConnectorHelper {
private JavaScriptObject connectorWrapper; private JavaScriptObject connectorWrapper;
private int tag; private int tag;


private boolean inited = false; private String initFunctionName;


public JavaScriptConnectorHelper(ServerConnector connector) { public JavaScriptConnectorHelper(ServerConnector connector) {
this.connector = connector; this.connector = connector;
Expand Down Expand Up @@ -96,9 +96,8 @@ public void onStateChanged(StateChangeEvent stateChangeEvent) {
} }


// Init after setting up callbacks & rpc // Init after setting up callbacks & rpc
if (!inited) { if (initFunctionName == null) {
initJavaScript(); initJavaScript();
inited = true;
} }


invokeIfPresent(wrapper, "onStateChange"); invokeIfPresent(wrapper, "onStateChange");
Expand All @@ -120,7 +119,7 @@ protected JavaScriptObject createRpcObject(String iface, Set<String> methods) {
return object; return object;
} }


private boolean initJavaScript() { protected boolean initJavaScript() {
ApplicationConfiguration conf = connector.getConnection() ApplicationConfiguration conf = connector.getConnection()
.getConfiguration(); .getConfiguration();
ArrayList<String> attemptedNames = new ArrayList<String>(); ArrayList<String> attemptedNames = new ArrayList<String>();
Expand All @@ -132,6 +131,7 @@ private boolean initJavaScript() {
if (tryInitJs(initFunctionName, getConnectorWrapper())) { if (tryInitJs(initFunctionName, getConnectorWrapper())) {
VConsole.log("JavaScript connector initialized using " VConsole.log("JavaScript connector initialized using "
+ initFunctionName); + initFunctionName);
this.initFunctionName = initFunctionName;
return true; return true;
} else { } else {
VConsole.log("No JavaScript function " + initFunctionName VConsole.log("No JavaScript function " + initFunctionName
Expand Down Expand Up @@ -160,7 +160,7 @@ private static native boolean tryInitJs(String initFunctionName,
} }
}-*/; }-*/;


private JavaScriptObject getConnectorWrapper() { public JavaScriptObject getConnectorWrapper() {
if (connectorWrapper == null) { if (connectorWrapper == null) {
connectorWrapper = createConnectorWrapper(this, connectorWrapper = createConnectorWrapper(this,
connector.getConnection(), nativeState, rpcMap, connector.getConnection(), nativeState, rpcMap,
Expand Down Expand Up @@ -465,4 +465,7 @@ private static native void invokeIfPresent(
} }
}-*/; }-*/;


public String getInitFunctionName() {
return initFunctionName;
}
} }
@@ -0,0 +1,278 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* 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.vaadin.client.connectors;

import java.util.ArrayList;
import java.util.Collection;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.dom.client.NativeEvent;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.JavaScriptConnectorHelper;
import com.vaadin.client.Util;
import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
import com.vaadin.client.renderers.ComplexRenderer;
import com.vaadin.client.renderers.Renderer;
import com.vaadin.client.widget.grid.CellReference;
import com.vaadin.client.widget.grid.RendererCellReference;
import com.vaadin.shared.JavaScriptExtensionState;
import com.vaadin.shared.ui.Connect;
import com.vaadin.ui.renderer.AbstractJavaScriptRenderer;

import elemental.json.JsonObject;
import elemental.json.JsonValue;

/**
* Connector for server-side renderer implemented using JavaScript.
*
* @since
* @author Vaadin Ltd
*/
@Connect(AbstractJavaScriptRenderer.class)
public class JavaScriptRendererConnector extends
AbstractRendererConnector<JsonValue> implements
HasJavaScriptConnectorHelper {
private final JavaScriptConnectorHelper helper = new JavaScriptConnectorHelper(
this);

private final JavaScriptObject cellReferenceWrapper = createCellReferenceWrapper(BrowserInfo
.get().isIE8());

@Override
protected void init() {
super.init();
helper.init();

addGetRowKey(helper.getConnectorWrapper());
}

private static native JavaScriptObject createCellReferenceWrapper(
boolean isIE8)
/*-{
var reference = {};
if (isIE8) {
// IE8 only supports defineProperty for DOM objects
reference = $doc.createElement('div');
}
var setProperty = function(name, getter, setter) {
var descriptor = {
get: getter
}
if (setter) {
descriptor.set = setter;
}
Object.defineProperty(reference, name, descriptor);
};
setProperty("element", function() {
return reference.target.@CellReference::getElement()();
}, null);
setProperty("rowIndex", function() {
return reference.target.@CellReference::getRowIndex()();
}, null);
setProperty("columnIndex", function() {
return reference.target.@CellReference::getColumnIndex()();
}, null);
setProperty("colSpan", function() {
return reference.target.@RendererCellReference::getColSpan()();
}, function(colSpan) {
reference.target.@RendererCellReference::setColSpan(*)(colSpan);
});
return reference;
}-*/;

@Override
public JavaScriptExtensionState getState() {
return (JavaScriptExtensionState) super.getState();
}

private native void addGetRowKey(JavaScriptObject wrapper)
/*-{
var self = this;
wrapper.getRowKey = $entry(function(rowIndex) {
return @JavaScriptRendererConnector::findRowKey(*)(self, rowIndex);
});
}-*/;

private static String findRowKey(JavaScriptRendererConnector connector,
int rowIndex) {
GridConnector gc = (GridConnector) connector.getParent();
JsonObject row = gc.getWidget().getDataSource().getRow(rowIndex);
return connector.getRowKey(row);
}

private boolean hasFunction(String name) {
return hasFunction(helper.getConnectorWrapper(), name);
}

private static native boolean hasFunction(JavaScriptObject wrapper,
String name)
/*-{
return typeof wrapper[name] === 'function';
}-*/;

@Override
protected Renderer<JsonValue> createRenderer() {
if (!hasFunction("render")) {
throw new RuntimeException("JavaScriptRenderer "
+ helper.getInitFunctionName()
+ " must have a function named 'render'");
}

final boolean hasInit = hasFunction("init");
final boolean hasDestroy = hasFunction("destroy");
final boolean hasOnActivate = hasFunction("onActivate");
final boolean hasGetConsumedEvents = hasFunction("getConsumedEvents");
final boolean hasOnBrowserEvent = hasFunction("onBrowserEvent");

return new ComplexRenderer<JsonValue>() {
@Override
public void render(RendererCellReference cell, JsonValue data) {
render(helper.getConnectorWrapper(), getJsCell(cell),
Util.json2jso(data));
}

private JavaScriptObject getJsCell(CellReference<?> cell) {
updateCellReference(cellReferenceWrapper, cell);
return cellReferenceWrapper;
}

public native void render(JavaScriptObject wrapper,
JavaScriptObject cell, JavaScriptObject data)
/*-{
wrapper.render(cell, data);
}-*/;

@Override
public void init(RendererCellReference cell) {
if (hasInit) {
init(helper.getConnectorWrapper(), getJsCell(cell));
}
}

private native void init(JavaScriptObject wrapper,
JavaScriptObject cell)
/*-{
wrapper.init(cell);
}-*/;

private native void updateCellReference(
JavaScriptObject cellWrapper, CellReference<?> target)
/*-{
cellWrapper.target = target;
}-*/;

@Override
public void destroy(RendererCellReference cell) {
if (hasDestroy) {
destory(helper.getConnectorWrapper(), getJsCell(cell));
} else {
super.destroy(cell);
}
}

private native void destory(JavaScriptObject wrapper,
JavaScriptObject cell)
/*-{
wrapper.destory(cell);
}-*/;

@Override
public boolean onActivate(CellReference<?> cell) {
if (hasOnActivate) {
return onActivate(helper.getConnectorWrapper(),
getJsCell(cell));
} else {
return super.onActivate(cell);
}
}

private native boolean onActivate(JavaScriptObject wrapper,
JavaScriptObject cell)
/*-{
return !!wrapper.onActivate(cell);
}-*/;

@Override
public Collection<String> getConsumedEvents() {
if (hasGetConsumedEvents) {
JsArrayString events = getConsumedEvents(helper
.getConnectorWrapper());

ArrayList<String> list = new ArrayList<String>(
events.length());
for (int i = 0; i < events.length(); i++) {
list.add(events.get(i));
}
return list;
} else {
return super.getConsumedEvents();
}
}

private native JsArrayString getConsumedEvents(
JavaScriptObject wrapper)
/*-{
var rawEvents = wrapper.getConsumedEvents();
var events = [];
for(var i = 0; i < rawEvents.length; i++) {
events[i] = ""+rawEvents[i];
}
return events;
}-*/;

@Override
public boolean onBrowserEvent(CellReference<?> cell,
NativeEvent event) {
if (hasOnBrowserEvent) {
return onBrowserEvent(helper.getConnectorWrapper(),
getJsCell(cell), event);
} else {
return super.onBrowserEvent(cell, event);
}
}

private native boolean onBrowserEvent(JavaScriptObject wrapper,
JavaScriptObject cell, NativeEvent event)
/*-{
return !!wrapper.onBrowserEvent(cell, event);
}-*/;
};
}

@Override
public JsonValue decode(JsonValue value) {
// Let the js logic decode the raw json that the server sent
return value;
}

@Override
public void onUnregister() {
super.onUnregister();
helper.onUnregister();
}

@Override
public JavaScriptConnectorHelper getJavascriptConnectorHelper() {
return helper;
}
}
Expand Up @@ -83,7 +83,7 @@ public void setColSpan(int numberOfCells) {
* *
* @return the number of columns that the cell should span * @return the number of columns that the cell should span
*/ */
public int getColspan() { public int getColSpan() {
return cell.getColSpan(); return cell.getColSpan();
} }
} }
4 changes: 2 additions & 2 deletions server/src/com/vaadin/server/AbstractJavaScriptExtension.java
Expand Up @@ -106,8 +106,8 @@
* <li>Java Dates are represented by JavaScript numbers containing the timestamp * <li>Java Dates are represented by JavaScript numbers containing the timestamp
* </li> * </li>
* <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li> * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
* <li>Map<String, ?> in Java is represented by JavaScript object with fields * <li>Map&lt;String, ?&gt; in Java is represented by JavaScript object with
* corresponding to the map keys.</li> * fields corresponding to the map keys.</li>
* <li>Any other Java Map is represented by a JavaScript array containing two * <li>Any other Java Map is represented by a JavaScript array containing two
* arrays, the first contains the keys and the second contains the values in the * arrays, the first contains the keys and the second contains the values in the
* same order.</li> * same order.</li>
Expand Down
4 changes: 2 additions & 2 deletions server/src/com/vaadin/ui/AbstractJavaScriptComponent.java
Expand Up @@ -119,8 +119,8 @@
* <li>Java Dates are represented by JavaScript numbers containing the timestamp * <li>Java Dates are represented by JavaScript numbers containing the timestamp
* </li> * </li>
* <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li> * <li>List, Set and all arrays in Java are represented by JavaScript arrays.</li>
* <li>Map<String, ?> in Java is represented by JavaScript object with fields * <li>Map&lt;String, ?&gt; in Java is represented by JavaScript object with
* corresponding to the map keys.</li> * fields corresponding to the map keys.</li>
* <li>Any other Java Map is represented by a JavaScript array containing two * <li>Any other Java Map is represented by a JavaScript array containing two
* arrays, the first contains the keys and the second contains the values in the * arrays, the first contains the keys and the second contains the values in the
* same order.</li> * same order.</li>
Expand Down

0 comments on commit 5102cc9

Please sign in to comment.