/*
* Copyright 2008 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 com.googlecode.gwt.charts.client.apiloader;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.ScriptElement;
import com.google.gwt.user.client.Window;
import java.util.Vector;
/**
* A wrapper for the <a href="https://developers.google.com/loader/">Google Loader/a>.
*/
public class ApiLoader {
private static String HOSTNAME = "www.google.com";
/**
* Launches an API load request.
*
* @param api the name of the API to load
* @param version the API version to load
* @param onLoad a callback that will be invoked when the API is finished
* loaded. Do not make any calls into the API being loaded until
* this call returns.
* @param optionalSettings an object containing additional settings.
*/
public static void loadApi(final String api, final String version, Runnable onLoad,
ApiLoaderOptions optionalSettings) {
ApiLoader loader = new ApiLoader();
// Set the onLoad callback into the
assert (onLoad != null);
ApiLoaderOptions settings = optionalSettings;
if (settings == null) {
settings = ApiLoaderOptions.create();
}
settings.setCallback(onLoad);
final ApiLoaderOptions copyOfSettings = settings;
// Define a Runnable that will run the actual load.
Runnable apiLoad = new Runnable() {
@Override
public void run() {
loadApi(api, version, copyOfSettings);
}
};
if (loader.loaded) {
// jsapi is finished loading, start the individual API load now.
apiLoad.run();
} else {
// Defer the load until jsapi is finished.
loader.queuedApiLoads.add(apiLoad);
}
}
/**
* Wrapper for ApiLoader google.load() native method.
*/
private static native void loadApi(String api, String version, JavaScriptObject settings) /*-{
$wnd.google.load(api, version, settings);
}-*/;
// NativeCreateCallback already ran, or someone injected the API outside of
// this program.
private boolean alreadyInjected = false;
// Set to true if the init(key) method has been called.
private boolean initialized = false;
// True if the JavaScript __gwt_charts_AjaxLoader_onLoad callback has already run.
// This function is registered on the window in createCallback()
private boolean loaded = false;
private Vector<Runnable> queuedApiLoads = new Vector<Runnable>();
/**
* Initialize the API without specifying a key.
*/
public ApiLoader() {
this(null);
}
/**
* Initialize the API with a supplied key value.<br>
* See https://code.google.com/apis/console
*
* @param apiKey API key value.
*/
public ApiLoader(String apiKey) {
if (initialized == true) {
return;
}
boolean alreadyLoaded = injectJsApi(apiKey);
// In IE, the above script can execute immediately if its already in the
// cache, so don't touch the loaded variable unless we bypassed loading
// the script completely
if (alreadyLoaded) {
loaded = true;
}
initialized = true;
}
/**
* Creates a function to be registered for a callback after jsapi loads.
*/
private native boolean createCallback(ApiLoader loader) /*-{
if ($wnd['google'] && $wnd.google['load']) {
// The API has already been loaded.
return true;
}
$wnd.__gwt_charts_AjaxLoader_onLoad = function() {
loader.@com.googlecode.gwt.charts.client.apiloader.ApiLoader::onLoadCallback()();
}
// The application must wait for a callback.
return false;
}-*/;
/**
* Adds a script element to the DOM that loads the API Loader main script "jsapi".
*
* @param apiKey
* Optional API key value (pass null to omit the key). See
* http://code.google.com/apis/ajaxsearch/signup.html
* @returns <code>true</code> if the API has already been loaded. Otherwise,
* returns <code>false</code>, meaning that the application should
* wait for a callback.
*/
private boolean injectJsApi(String apiKey) {
if (alreadyInjected) {
return true;
}
boolean alreadyLoaded = createCallback(this);
alreadyInjected = true;
if (alreadyLoaded) {
return true;
}
Document doc = Document.get();
String key = (apiKey == null) ? "" : ("key=" + apiKey + "&");
String protocol = (Window.Location.getProtocol().equals("https:")) ? "https:" : "http:";
String src = protocol + "//" + HOSTNAME + "/jsapi?" + key + "callback=__gwt_charts_AjaxLoader_onLoad";
ScriptElement script = doc.createScriptElement();
script.setSrc(src);
script.setType("text/javascript");
doc.getBody().appendChild(script);
return false;
}
/**
* Called back when the jsapi is finished loaded. It must kick of any API
* loads that have been queued while waiting on jsapi to finish loading.
*/
protected void onLoadCallback() {
loaded = true;
for (Runnable r : queuedApiLoads) {
r.run();
}
queuedApiLoads.clear();
}
}