/**
* Copyright 2010 Google Inc.
*
* 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 org.waveprotocol.wave.client.doodad.experimental.htmltemplate;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.IFrameElement;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.TextResource;
import com.google.gwt.user.client.ui.HTML;
import org.waveprotocol.wave.client.doodad.experimental.htmltemplate.CajoleService.CajolerResponse;
import org.waveprotocol.wave.client.scheduler.Scheduler.Task;
import org.waveprotocol.wave.client.scheduler.SchedulerInstance;
import org.waveprotocol.wave.model.util.CollectionUtils;
import org.waveprotocol.wave.model.util.StringMap;
/**
* Interface to the Caja machinery that prepares the surrounding native HTML
* page and instantiates each HtmlTemplate doodad within a GWT HTML widget
* supplied by the client.
*
* @author jasvir@google.com (Jasvir Nagra)
* @author ihab@google.com (Ihab Awad)
*/
class CajolerFacade {
interface Css extends CssResource {
String outerHull();
String innerHull();
}
interface Resources extends ClientBundle {
@Source("secureStyles.css")
Css secureStyles();
@Source("supporting_js.jslib")
TextResource supportingJs();
@Source("taming.js")
TextResource taming();
}
private static final Resources RESOURCES = GWT.create(Resources.class);
static {
StyleInjector.inject(RESOURCES.secureStyles().getText(), true);
}
// Enable to true once tested more thoroughly.
private static final CajolerFacade instance = create();
// Enable once a cache replacement policy is established. Timeout?
private static final boolean ENABLE_CACHE = false;
private final CajoleService cajoleService;
private final IFrameElement cajaFrame;
private final StringMap<CajolerResponse> cache = CollectionUtils.createStringMap();
public static CajolerFacade instance() {
return instance;
}
private static CajolerFacade create() {
IFrameElement cajaFrame = createCajaFrame();
CajoleService service = new HttpCajoleService();
return new CajolerFacade(service, cajaFrame);
}
private static IFrameElement createCajaFrame() {
IFrameElement cajaFrame = Document.get().createIFrameElement();
cajaFrame.setFrameBorder(0);
cajaFrame.setAttribute("width", "0");
cajaFrame.setAttribute("height", "0");
Document.get().getBody().appendChild(cajaFrame);
Document cajaFrameDoc = cajaFrame.getContentDocument();
cajaFrameDoc.getBody().appendChild(
cajaFrameDoc.createScriptElement(RESOURCES.supportingJs().getText()));
cajaFrameDoc.getBody().appendChild(
cajaFrameDoc.createScriptElement(RESOURCES.taming().getText()));
return cajaFrame;
}
private CajolerFacade(CajoleService service, IFrameElement cajaFrame) {
this.cajoleService = service;
this.cajaFrame = cajaFrame;
}
private void appendToDocument(HTML target, PluginContext pluginContext, CajolerResponse response) {
DivElement domitaVdocElement = Document.get().createDivElement();
domitaVdocElement.setClassName("innerHull");
target.getElement().setInnerHTML("");
target.getElement().setClassName("outerHull");
target.getElement().appendChild(domitaVdocElement);
initializeDoodadEnvironment(
cajaFrame, domitaVdocElement, pluginContext.getJSOInterface());
// Render HTML
domitaVdocElement.setInnerHTML(response.getHtml());
// Inject JS
Document cajaFrameDoc = cajaFrame.getContentDocument();
cajaFrameDoc.getBody().appendChild(cajaFrameDoc.createScriptElement(response.getJs()));
}
private static native void initializeDoodadEnvironment(
Element cajaFrame, Element domitaVdocElement, JavaScriptObject wave) /*-{
cajaFrame.contentWindow.caja___.initialize(domitaVdocElement, wave);
}-*/;
private void handleSuccessfulResponse(
String url, HTML target, PluginContext pluginContext, CajolerResponse cajoled) {
if (ENABLE_CACHE) {
cache.put(url, cajoled);
}
appendToDocument(target, pluginContext, cajoled);
}
/**
* Given a GWT {@code HTML} widget, instantiate an HTML Template doodad within
* that widget.
*
* @param target a GWT {@code HTML} widget. The contents of this widget will
* be cleared out and replaced with the dynamic content created by the
* HTML Template doodad.
* @param pluginContext an object which will be exposed to the HTML Template
* doodad as a global window variable named {@code wave}. This
* represents the Wave API to the doodad's JavaScript.
* @param url the URL of an HTML file that will be cajoled (via Caja) and
* instantiated as the contents of the supplied {@code HTML} widget.
*/
// This should be @SuppressWarnings("deadCode"), to ignore the branch disabled
// by the ENABLE_CACHE value, but Eclipse does not recognise "deadCode".
@SuppressWarnings("all")
public void instantiateDoodadInHTML(
final HTML target, final PluginContext pluginContext, final String url) {
if (ENABLE_CACHE && cache.containsKey(url)) {
SchedulerInstance.getMediumPriorityTimer().schedule(new Task() {
@Override
public void execute() {
// Use isAttached as a cheap approximation of doodad still being active.
if (target.isAttached()) {
appendToDocument(target, pluginContext, cache.get(url));
}
}
});
} else {
cajoleService.cajole(url, new Callback<CajolerResponse>() {
@Override
public void onSuccess(CajolerResponse cajoled) {
handleSuccessfulResponse(url, target, pluginContext, cajoled);
}
@Override
public void onError(String message) {
// log.
}
});
}
}
public JavaScriptObject getTaming() {
return doGetCajaObject(cajaFrame);
}
private static native JavaScriptObject doGetCajaObject(JavaScriptObject cajaFrame) /*-{
return cajaFrame.contentWindow.caja___;
}-*/;
}