package com.project.shared.client.net;
import java.util.HashMap;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.LinkElement;
import com.google.gwt.dom.client.ScriptElement;
import com.project.shared.client.events.SimpleEvent;
import com.project.shared.client.events.SimpleEvent.Handler;
import com.project.shared.client.events.SingleEvent;
import com.project.shared.client.utils.HandlerUtils;
import com.project.shared.data.funcs.AsyncFunc;
import com.project.shared.data.funcs.Func;
public class DynamicSourceLoader
{
private final SimpleEvent.Handler<Void> handler;
private DynamicSourceLoader(String source, SimpleEvent.Handler<Void> handler)
{
this.handler = handler;
String normalizedSource = source.toLowerCase().trim();
Element elem = null;
// TODO: replace the endsWith heuristic with a parameter that tells what type of element to create or a function that creates the proper element
if (normalizedSource.endsWith(".css"))
{
LinkElement linkElem = Document.get().createLinkElement();
linkElem.setHref(source);
linkElem.setRel("stylesheet");
linkElem.setType("text/css");
elem = linkElem;
}
else {
// If it isn't css, assume it's javascript (sometimes javascript urls don't end with .js because they are queries that dynamically generate js)
ScriptElement scriptElem = Document.get().createScriptElement();
scriptElem.setSrc(source);
// scriptElem.setLang("javascript"); // lang is deprecated?
scriptElem.setType("text/javascript");
elem = scriptElem;
}
this.registerLoadedHandler(elem);
Document.get().getElementsByTagName("head").getItem(0).appendChild(elem);
}
void scriptLoaded()
{
this.handler.onFire(null);
}
private native final void registerLoadedHandler(Element elem)
/*-{
var me = this;
elem.onload = function() {
me.@com.project.shared.client.net.DynamicSourceLoader::scriptLoaded()();
};
}-*/;
private final static HashMap<String, Boolean> scriptLoadStatusMap = new HashMap<String, Boolean>();
private final static HashMap<String, SingleEvent<Void>> scriptLoadHandlersMap = new HashMap<String, SingleEvent<Void>>();
public static void load(final String source, final SimpleEvent.Handler<Void> handler)
{
getLoadAsyncFunc(source).then(HandlerUtils.toFunc(handler))
.run(null);
}
public static AsyncFunc<Void, Void> getLoadAsyncFunc(final String source)
{
return new AsyncFunc<Void, Void>() {
@Override
protected <S, E> void run(Void arg, final Func<Void, S> successHandler, final Func<Throwable, E> errorHandler)
{
final Handler<Void> innerHandler = HandlerUtils.fromFunc(successHandler);
Boolean status = scriptLoadStatusMap.get(source);
if (null == status) {
scriptLoadStatusMap.put(source, false);
scriptLoadHandlersMap.put(source, new SingleEvent<Void>());
// Do the script-element creation and handling,
// but start the process in a deferred command
// so it doesn't stop the page from loading if it hasn't
// finished yet.
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute()
{
loadSource(source, innerHandler);
}
});
return;
}
if (false == status) {
// not the first time someone wants to load this source
// but it didn't load yet.
scriptLoadHandlersMap.get(source).addHandler(innerHandler);
return;
}
// Already loaded. Fire the handler in a deferred scheduler.
// (for consistently async behavior)
HandlerUtils.fireDeferred(HandlerUtils.fromFunc(successHandler), null);
}
};
}
private static void loadSource(final String source, final SimpleEvent.Handler<Void> handler)
{
// no entry exists for this source -
// first load request
SingleEvent<Void> event = scriptLoadHandlersMap.get(source);
event.addHandler(handler);
Handler<Void> wrappedHandler = new SimpleEvent.Handler<Void>() {
@Override
public void onFire(Void arg)
{
sourceLoaded(source);
}
};
new DynamicSourceLoader(source, wrappedHandler);
}
private static void sourceLoaded(final String source)
{
scriptLoadStatusMap.put(source, true);
scriptLoadHandlersMap.get(source).dispatch(null);
}
}